test: port tests

This commit is contained in:
daiwei 2025-04-10 11:40:54 +08:00
parent a103715cfb
commit c6e3fab3de
2 changed files with 224 additions and 13 deletions

View File

@ -686,5 +686,222 @@ describe('VaporKeepAlive', () => {
test('include + exclude', async () => {
await assertNameMatch({ include: () => 'one,two', exclude: () => 'two' })
})
test('max', async () => {
const spyAC = vi.fn()
const spyBC = vi.fn()
const spyCC = vi.fn()
const spyAA = vi.fn()
const spyBA = vi.fn()
const spyCA = vi.fn()
const spyADA = vi.fn()
const spyBDA = vi.fn()
const spyCDA = vi.fn()
const spyAUM = vi.fn()
const spyBUM = vi.fn()
const spyCUM = vi.fn()
function assertCount(calls: number[]) {
expect([
spyAC.mock.calls.length,
spyAA.mock.calls.length,
spyADA.mock.calls.length,
spyAUM.mock.calls.length,
spyBC.mock.calls.length,
spyBA.mock.calls.length,
spyBDA.mock.calls.length,
spyBUM.mock.calls.length,
spyCC.mock.calls.length,
spyCA.mock.calls.length,
spyCDA.mock.calls.length,
spyCUM.mock.calls.length,
]).toEqual(calls)
}
const viewRef = ref('a')
const views: Record<string, VaporComponent> = {
a: defineVaporComponent({
name: 'a',
setup() {
onBeforeMount(() => spyAC())
onActivated(() => spyAA())
onDeactivated(() => spyADA())
onUnmounted(() => spyAUM())
return template(`one`)()
},
}),
b: defineVaporComponent({
name: 'b',
setup() {
onBeforeMount(() => spyBC())
onActivated(() => spyBA())
onDeactivated(() => spyBDA())
onUnmounted(() => spyBUM())
return template(`two`)()
},
}),
c: defineVaporComponent({
name: 'c',
setup() {
onBeforeMount(() => spyCC())
onActivated(() => spyCA())
onDeactivated(() => spyCDA())
onUnmounted(() => spyCUM())
return template(`three`)()
},
}),
}
define({
setup() {
return createComponent(
VaporKeepAlive,
{ max: () => 2 },
{
default: () => createDynamicComponent(() => views[viewRef.value]),
},
)
},
}).render()
assertCount([1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
viewRef.value = 'b'
await nextTick()
assertCount([1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0])
viewRef.value = 'c'
await nextTick()
// should prune A because max cache reached
assertCount([1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0])
viewRef.value = 'b'
await nextTick()
// B should be reused, and made latest
assertCount([1, 1, 1, 1, 1, 2, 1, 0, 1, 1, 1, 0])
viewRef.value = 'a'
await nextTick()
// C should be pruned because B was used last so C is the oldest cached
assertCount([2, 2, 1, 1, 1, 2, 2, 0, 1, 1, 1, 1])
})
})
describe('cache invalidation', () => {
function setup() {
const viewRef = ref('one')
const includeRef = ref('one,two')
define({
setup() {
return createComponent(
VaporKeepAlive,
{ include: () => includeRef.value },
{
default: () => createDynamicComponent(() => views[viewRef.value]),
},
)
},
}).render()
return { viewRef, includeRef }
}
function setupExclude() {
const viewRef = ref('one')
const excludeRef = ref('')
define({
setup() {
return createComponent(
VaporKeepAlive,
{ exclude: () => excludeRef.value },
{
default: () => createDynamicComponent(() => views[viewRef.value]),
},
)
},
}).render()
return { viewRef, excludeRef }
}
test('on include change', async () => {
const { viewRef, includeRef } = setup()
viewRef.value = 'two'
await nextTick()
assertHookCalls(oneHooks, [1, 1, 1, 1, 0])
assertHookCalls(twoHooks, [1, 1, 1, 0, 0])
includeRef.value = 'two'
await nextTick()
assertHookCalls(oneHooks, [1, 1, 1, 1, 1])
assertHookCalls(twoHooks, [1, 1, 1, 0, 0])
viewRef.value = 'one'
await nextTick()
assertHookCalls(oneHooks, [2, 2, 1, 1, 1])
assertHookCalls(twoHooks, [1, 1, 1, 1, 0])
})
test('on exclude change', async () => {
const { viewRef, excludeRef } = setupExclude()
viewRef.value = 'two'
await nextTick()
assertHookCalls(oneHooks, [1, 1, 1, 1, 0])
assertHookCalls(twoHooks, [1, 1, 1, 0, 0])
excludeRef.value = 'one'
await nextTick()
assertHookCalls(oneHooks, [1, 1, 1, 1, 1])
assertHookCalls(twoHooks, [1, 1, 1, 0, 0])
viewRef.value = 'one'
await nextTick()
assertHookCalls(oneHooks, [2, 2, 1, 1, 1])
assertHookCalls(twoHooks, [1, 1, 1, 1, 0])
})
test('on include change + view switch', async () => {
const { viewRef, includeRef } = setup()
viewRef.value = 'two'
await nextTick()
assertHookCalls(oneHooks, [1, 1, 1, 1, 0])
assertHookCalls(twoHooks, [1, 1, 1, 0, 0])
includeRef.value = 'one'
viewRef.value = 'one'
await nextTick()
assertHookCalls(oneHooks, [1, 1, 2, 1, 0])
// two should be pruned
assertHookCalls(twoHooks, [1, 1, 1, 1, 1])
})
test('on exclude change + view switch', async () => {
const { viewRef, excludeRef } = setupExclude()
viewRef.value = 'two'
await nextTick()
assertHookCalls(oneHooks, [1, 1, 1, 1, 0])
assertHookCalls(twoHooks, [1, 1, 1, 0, 0])
excludeRef.value = 'two'
viewRef.value = 'one'
await nextTick()
assertHookCalls(oneHooks, [1, 1, 2, 1, 0])
// two should be pruned
assertHookCalls(twoHooks, [1, 1, 1, 1, 1])
})
test('should not prune current active instance', async () => {
const { viewRef, includeRef } = setup()
includeRef.value = 'two'
await nextTick()
assertHookCalls(oneHooks, [1, 1, 1, 0, 0])
assertHookCalls(twoHooks, [0, 0, 0, 0, 0])
viewRef.value = 'two'
await nextTick()
assertHookCalls(oneHooks, [1, 1, 1, 0, 1])
assertHookCalls(twoHooks, [1, 1, 1, 0, 0])
})
})
})

View File

@ -62,9 +62,8 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
;(keepAliveInstance as any).__v_cache = cache
}
const { include, exclude, max } = props
function shouldCache(instance: VaporComponentInstance) {
const { include, exclude } = props
const name = getComponentName(instance.type)
return !(
(include && (!name || !matches(include, name))) ||
@ -73,6 +72,7 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
}
function cacheBlock() {
const { max } = props
// TODO suspense
const currentBlock = keepAliveInstance.block!
if (!isValidBlock(currentBlock)) return
@ -119,15 +119,6 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
instance.shapeFlag! |= ShapeFlags.COMPONENT_KEPT_ALIVE
}
// const name = getComponentName(instance.type)
// if (
// !(
// (include && (!name || !matches(include, name))) ||
// (exclude && name && matches(exclude, name))
// )
// ) {
// instance.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
// }
if (shouldCache(instance)) {
instance.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
}
@ -138,7 +129,7 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
parentNode: ParentNode,
anchor: Node,
) => {
const cachedBlock = cache.get(instance.type)!
const cachedBlock = (current = cache.get(instance.type)!)
insert((instance.block = cachedBlock.block), parentNode, anchor)
queuePostFlushCb(() => {
instance.isDeactivated = false
@ -182,7 +173,10 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
function pruneCacheEntry(key: CacheKey) {
const cached = cache.get(key)
if (cached) {
unmountComponent(cached)
resetShapeFlag(cached)
if (cached !== current) {
unmountComponent(cached)
}
}
cache.delete(key)
keys.delete(key)