Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e2cf58a

Browse files
committed
fix(runtime-vapor): prune KeepAlive composite cache keys
1 parent 5239691 commit e2cf58a

2 files changed

Lines changed: 134 additions & 6 deletions

File tree

‎packages/runtime-vapor/__tests__/components/KeepAlive.spec.ts‎

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1765,6 +1765,101 @@ describe('VaporKeepAlive', () => {
17651765
expect(keptAliveScopes.size).toBe(0)
17661766
})
17671767

1768+
test('should recreate composite cache key after max prunes keyed branch entry', async () => {
1769+
const Comp = defineVaporComponent({
1770+
name: 'Comp',
1771+
props: ['id'],
1772+
setup() {
1773+
return template('<div></div>')()
1774+
},
1775+
})
1776+
1777+
const toggle = ref(true)
1778+
const { instance } = define({
1779+
setup() {
1780+
return createComponent(
1781+
VaporKeepAlive,
1782+
{ max: () => 1 },
1783+
{
1784+
default: () =>
1785+
// index=0 makes this a keyed DynamicFragment
1786+
createIf(
1787+
() => toggle.value,
1788+
() => createComponent(Comp, { id: () => 'a' }),
1789+
() => createComponent(Comp, { id: () => 'b' }),
1790+
undefined,
1791+
undefined,
1792+
0,
1793+
),
1794+
},
1795+
)
1796+
},
1797+
}).render()
1798+
1799+
const keepAliveInstance = instance!.block as any
1800+
const cache = keepAliveInstance.__v_cache as Map<any, any>
1801+
1802+
await nextTick()
1803+
expect(cache.size).toBe(1)
1804+
const keyA1 = Array.from(cache.keys())[0]
1805+
1806+
toggle.value = false
1807+
await nextTick()
1808+
expect(cache.size).toBe(1)
1809+
const keyB = Array.from(cache.keys())[0]
1810+
expect(keyB).not.toBe(keyA1)
1811+
1812+
toggle.value = true
1813+
await nextTick()
1814+
expect(cache.size).toBe(1)
1815+
const keyA2 = Array.from(cache.keys())[0]
1816+
1817+
expect((keyA1 as any).branchKey).toBe((keyA2 as any).branchKey)
1818+
expect(keyA2).not.toBe(keyA1)
1819+
})
1820+
1821+
test('should recreate composite cache key after KeepAlive hmr rerender', async () => {
1822+
const Comp = defineVaporComponent({
1823+
name: 'Comp',
1824+
props: ['id'],
1825+
setup() {
1826+
return template('<div></div>')()
1827+
},
1828+
})
1829+
1830+
const toggle = ref(true)
1831+
const { instance } = define({
1832+
setup() {
1833+
return createComponent(VaporKeepAlive, null, {
1834+
default: () =>
1835+
createIf(
1836+
() => toggle.value,
1837+
() => createComponent(Comp, { id: () => 'a' }),
1838+
() => createComponent(Comp, { id: () => 'b' }),
1839+
undefined,
1840+
undefined,
1841+
0,
1842+
),
1843+
})
1844+
},
1845+
}).render()
1846+
1847+
const keepAliveInstance = instance!.block as any
1848+
const cache = keepAliveInstance.__v_cache as Map<any, any>
1849+
1850+
await nextTick()
1851+
expect(cache.size).toBe(1)
1852+
const keyA1 = Array.from(cache.keys())[0]
1853+
1854+
keepAliveInstance.hmrRerender!()
1855+
await nextTick()
1856+
1857+
expect(cache.size).toBe(1)
1858+
const keyA2 = Array.from(cache.keys())[0]
1859+
expect((keyA1 as any).branchKey).toBe((keyA2 as any).branchKey)
1860+
expect(keyA2).not.toBe(keyA1)
1861+
})
1862+
17681863
test('handle error in async onActivated', async () => {
17691864
const err = new Error('foo')
17701865
const handler = vi.fn()

‎packages/runtime-vapor/src/components/KeepAlive.ts‎

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,20 +79,24 @@ export interface KeepAliveInstance extends VaporComponentInstance {
7979
}
8080
}
8181

82-
type CacheKey = VaporComponent | VNode['type']
82+
const COMPOSITE_KEY: unique symbol = Symbol('keepAliveCompositeKey')
83+
84+
type BaseCacheKey = VaporComponent | VNode['type']
85+
type CacheKey = BaseCacheKey | CompositeKey
8386
type Cache = Map<CacheKey, VaporComponentInstance | VaporFragment>
8487
type Keys = Set<CacheKey>
8588
type CompositeKey = {
86-
type: CacheKey
89+
type: BaseCacheKey
8790
branchKey: any
91+
[COMPOSITE_KEY]: true
8892
}
8993

9094
// Returns a stable composite key object for a given (type, branchKey) pair.
9195
// Caches are passed as parameters (per KeepAlive instance) to avoid
9296
// module-level Map leaks for primitive type keys (e.g. string tag names
9397
// from VDOM interop).
9498
function getCompositeKey(
95-
type: CacheKey,
99+
type: BaseCacheKey,
96100
branchKey: any,
97101
compositeKeyCache: WeakMap<object, Map<any, CompositeKey>>,
98102
compositeKeyCachePrimitive: Map<any, Map<any, CompositeKey>>,
@@ -109,12 +113,37 @@ function getCompositeKey(
109113

110114
let composite = perType.get(branchKey)
111115
if (!composite) {
112-
composite = { type, branchKey }
116+
composite = { type, branchKey, [COMPOSITE_KEY]: true }
113117
perType.set(branchKey, composite)
114118
}
115119
return composite
116120
}
117121

122+
function deleteCompositeKey(
123+
key: CacheKey,
124+
compositeKeyCache: WeakMap<object, Map<any, CompositeKey>>,
125+
compositeKeyCachePrimitive: Map<any, Map<any, CompositeKey>>,
126+
): void {
127+
if (!isObject(key) || !(COMPOSITE_KEY in key)) return
128+
129+
const { type, branchKey } = key as CompositeKey
130+
const isObjectType = isObject(type) || isFunction(type)
131+
const perType = isObjectType
132+
? compositeKeyCache.get(type)
133+
: compositeKeyCachePrimitive.get(type)
134+
135+
if (!perType || perType.get(branchKey) !== key) return
136+
137+
perType.delete(branchKey)
138+
if (perType.size) return
139+
140+
if (isObjectType) {
141+
compositeKeyCache.delete(type)
142+
} else {
143+
compositeKeyCachePrimitive.delete(type)
144+
}
145+
}
146+
118147
const VaporKeepAliveImpl = defineVaporComponent({
119148
name: 'VaporKeepAlive',
120149
__isKeepAlive: true,
@@ -150,7 +179,7 @@ const VaporKeepAliveImpl = defineVaporComponent({
150179
const compositeKeyCachePrimitive = new Map<any, Map<any, CompositeKey>>()
151180

152181
const resolveKey = (
153-
type: VaporComponent | VNode['type'],
182+
type: BaseCacheKey,
154183
key?: any,
155184
branchKey?: any,
156185
): CacheKey => {
@@ -205,7 +234,10 @@ const VaporKeepAliveImpl = defineVaporComponent({
205234
const rerender = keepAliveInstance.hmrRerender
206235
keepAliveInstance.hmrRerender = () => {
207236
keepAliveInstance.exposed = null
208-
cache.forEach(cached => resetCachedShapeFlag(cached))
237+
cache.forEach((cached, key) => {
238+
resetCachedShapeFlag(cached)
239+
deleteCompositeKey(key, compositeKeyCache, compositeKeyCachePrimitive)
240+
})
209241
cache.clear()
210242
keys.clear()
211243
keptAliveScopes.forEach(scope => scope.stop())
@@ -351,6 +383,7 @@ const VaporKeepAliveImpl = defineVaporComponent({
351383
}
352384
cache.delete(key)
353385
keys.delete(key)
386+
deleteCompositeKey(key, compositeKeyCache, compositeKeyCachePrimitive)
354387
const scope = deleteScope(key)
355388
if (scope) scope.stop()
356389
}

0 commit comments

Comments
 (0)