From 3653bc0f45d6fedf84e29b64ca52584359c383c0 Mon Sep 17 00:00:00 2001 From: btea <2356281422@qq.com> Date: Tue, 13 Aug 2024 22:07:35 +0800 Subject: [PATCH] fix(keep-alive): ensure include/exclude regexp work with global flag (#11595) --- .../__tests__/components/KeepAlive.spec.ts | 144 ++++++++++++++++++ .../runtime-core/src/components/KeepAlive.ts | 1 + 2 files changed, 145 insertions(+) diff --git a/packages/runtime-core/__tests__/components/KeepAlive.spec.ts b/packages/runtime-core/__tests__/components/KeepAlive.spec.ts index 6d3f6a9b8b6..0b761972c00 100644 --- a/packages/runtime-core/__tests__/components/KeepAlive.spec.ts +++ b/packages/runtime-core/__tests__/components/KeepAlive.spec.ts @@ -32,6 +32,7 @@ const timeout = (n: number = 0) => new Promise(r => setTimeout(r, n)) describe('KeepAlive', () => { let one: ComponentOptions let two: ComponentOptions + let oneTest: ComponentOptions let views: Record let root: TestElement @@ -49,6 +50,18 @@ describe('KeepAlive', () => { deactivated: vi.fn(), unmounted: vi.fn(), } + oneTest = { + name: 'oneTest', + data: () => ({ msg: 'oneTest' }), + render(this: any) { + return h('div', this.msg) + }, + created: vi.fn(), + mounted: vi.fn(), + activated: vi.fn(), + deactivated: vi.fn(), + unmounted: vi.fn(), + } two = { name: 'two', data: () => ({ msg: 'two' }), @@ -63,6 +76,7 @@ describe('KeepAlive', () => { } views = { one, + oneTest, two, } }) @@ -369,6 +383,128 @@ describe('KeepAlive', () => { assertHookCalls(two, [2, 2, 0, 0, 2]) } + async function assertNameMatchWithFlag(props: KeepAliveProps) { + const outerRef = ref(true) + const viewRef = ref('one') + const App = { + render() { + return outerRef.value + ? h(KeepAlive, props, () => h(views[viewRef.value])) + : null + }, + } + render(h(App), root) + + expect(serializeInner(root)).toBe(`
one
`) + assertHookCalls(one, [1, 1, 1, 0, 0]) + assertHookCalls(oneTest, [0, 0, 0, 0, 0]) + assertHookCalls(two, [0, 0, 0, 0, 0]) + + viewRef.value = 'oneTest' + await nextTick() + expect(serializeInner(root)).toBe(`
oneTest
`) + assertHookCalls(one, [1, 1, 1, 1, 0]) + assertHookCalls(oneTest, [1, 1, 1, 0, 0]) + assertHookCalls(two, [0, 0, 0, 0, 0]) + + viewRef.value = 'two' + await nextTick() + expect(serializeInner(root)).toBe(`
two
`) + assertHookCalls(one, [1, 1, 1, 1, 0]) + assertHookCalls(oneTest, [1, 1, 1, 1, 0]) + assertHookCalls(two, [1, 1, 0, 0, 0]) + + viewRef.value = 'one' + await nextTick() + expect(serializeInner(root)).toBe(`
one
`) + assertHookCalls(one, [1, 1, 2, 1, 0]) + assertHookCalls(oneTest, [1, 1, 1, 1, 0]) + assertHookCalls(two, [1, 1, 0, 0, 1]) + + viewRef.value = 'oneTest' + await nextTick() + expect(serializeInner(root)).toBe(`
oneTest
`) + assertHookCalls(one, [1, 1, 2, 2, 0]) + assertHookCalls(oneTest, [1, 1, 2, 1, 0]) + assertHookCalls(two, [1, 1, 0, 0, 1]) + + viewRef.value = 'two' + await nextTick() + expect(serializeInner(root)).toBe(`
two
`) + assertHookCalls(one, [1, 1, 2, 2, 0]) + assertHookCalls(oneTest, [1, 1, 2, 2, 0]) + assertHookCalls(two, [2, 2, 0, 0, 1]) + + // teardown + outerRef.value = false + await nextTick() + expect(serializeInner(root)).toBe(``) + assertHookCalls(one, [1, 1, 2, 2, 1]) + assertHookCalls(oneTest, [1, 1, 2, 2, 1]) + assertHookCalls(two, [2, 2, 0, 0, 2]) + } + + async function assertNameMatchWithFlagExclude(props: KeepAliveProps) { + const outerRef = ref(true) + const viewRef = ref('one') + const App = { + render() { + return outerRef.value + ? h(KeepAlive, props, () => h(views[viewRef.value])) + : null + }, + } + render(h(App), root) + + expect(serializeInner(root)).toBe(`
one
`) + assertHookCalls(one, [1, 1, 0, 0, 0]) + assertHookCalls(oneTest, [0, 0, 0, 0, 0]) + assertHookCalls(two, [0, 0, 0, 0, 0]) + + viewRef.value = 'oneTest' + await nextTick() + expect(serializeInner(root)).toBe(`
oneTest
`) + assertHookCalls(one, [1, 1, 0, 0, 1]) + assertHookCalls(oneTest, [1, 1, 0, 0, 0]) + assertHookCalls(two, [0, 0, 0, 0, 0]) + + viewRef.value = 'two' + await nextTick() + expect(serializeInner(root)).toBe(`
two
`) + assertHookCalls(one, [1, 1, 0, 0, 1]) + assertHookCalls(oneTest, [1, 1, 0, 0, 1]) + assertHookCalls(two, [1, 1, 1, 0, 0]) + + viewRef.value = 'one' + await nextTick() + expect(serializeInner(root)).toBe(`
one
`) + assertHookCalls(one, [2, 2, 0, 0, 1]) + assertHookCalls(oneTest, [1, 1, 0, 0, 1]) + assertHookCalls(two, [1, 1, 1, 1, 0]) + + viewRef.value = 'oneTest' + await nextTick() + expect(serializeInner(root)).toBe(`
oneTest
`) + assertHookCalls(one, [2, 2, 0, 0, 2]) + assertHookCalls(oneTest, [2, 2, 0, 0, 1]) + assertHookCalls(two, [1, 1, 1, 1, 0]) + + viewRef.value = 'two' + await nextTick() + expect(serializeInner(root)).toBe(`
two
`) + assertHookCalls(one, [2, 2, 0, 0, 2]) + assertHookCalls(oneTest, [2, 2, 0, 0, 2]) + assertHookCalls(two, [1, 1, 2, 1, 0]) + + // teardown + outerRef.value = false + await nextTick() + expect(serializeInner(root)).toBe(``) + assertHookCalls(one, [2, 2, 0, 0, 2]) + assertHookCalls(oneTest, [2, 2, 0, 0, 2]) + assertHookCalls(two, [1, 1, 2, 2, 1]) + } + describe('props', () => { test('include (string)', async () => { await assertNameMatch({ include: 'one' }) @@ -378,6 +514,10 @@ describe('KeepAlive', () => { await assertNameMatch({ include: /^one$/ }) }) + test('include (regex with g flag)', async () => { + await assertNameMatchWithFlag({ include: /one/g }) + }) + test('include (array)', async () => { await assertNameMatch({ include: ['one'] }) }) @@ -390,6 +530,10 @@ describe('KeepAlive', () => { await assertNameMatch({ exclude: /^two$/ }) }) + test('exclude (regex with a flag)', async () => { + await assertNameMatchWithFlagExclude({ exclude: /one/g }) + }) + test('exclude (array)', async () => { await assertNameMatch({ exclude: ['two'] }) }) diff --git a/packages/runtime-core/src/components/KeepAlive.ts b/packages/runtime-core/src/components/KeepAlive.ts index e9c8cce58df..11be59ffcaf 100644 --- a/packages/runtime-core/src/components/KeepAlive.ts +++ b/packages/runtime-core/src/components/KeepAlive.ts @@ -382,6 +382,7 @@ function matches(pattern: MatchPattern, name: string): boolean { } else if (isString(pattern)) { return pattern.split(',').includes(name) } else if (isRegExp(pattern)) { + pattern.lastIndex = 0 return pattern.test(name) } /* istanbul ignore next */