From 9b6ecdeace96a6aa1fb825335fcfd66f9f49b837 Mon Sep 17 00:00:00 2001 From: daiwei Date: Wed, 19 Feb 2025 22:01:35 +0800 Subject: [PATCH 1/2] fix(runtime-core): handle KeepAlive children unmount when wrapped in stable v-for --- .../__tests__/rendererOptimizedMode.spec.ts | 49 +++++++++++++++++++ packages/runtime-core/src/renderer.ts | 2 +- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts b/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts index 958c1274806..5496cd83058 100644 --- a/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts +++ b/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts @@ -1,10 +1,12 @@ import { Fragment, type FunctionalComponent, + KeepAlive, type SetupContext, Teleport, type TestElement, type VNode, + computed, createApp, createBlock, createCommentVNode, @@ -1294,4 +1296,51 @@ describe('renderer: optimized mode', () => { expect(inner(root)).toBe('') expect(beforeUnmountSpy).toHaveBeenCalledTimes(1) }) + + //#12914 + test('unmount KeepAlive children when wrapped in v-for with stable fragment', async () => { + const CompA = { + setup() { + return () => h('span', 'CompA') + }, + } + const CompB = { + setup() { + return () => h('span', 'CompB') + }, + } + + const toggle = ref(true) + const view = computed(() => { + return toggle.value ? CompA : CompB + }) + + const app = createApp({ + render() { + return ( + openBlock(), + createElementBlock( + Fragment, + null, + renderList(1, () => { + return createVNode( + KeepAlive, + null, + [(openBlock(), createBlock(view.value))], + 1024 /* DYNAMIC_SLOTS */, + ) + }), + 64 /* STABLE_FRAGMENT */, + ) + ) + }, + }) + + app.mount(root) + expect(inner(root)).toBe('CompA') + + toggle.value = false + await nextTick() + expect(inner(root)).toBe('CompB') + }) }) diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 05c4ac345eb..ccf99f6b4a6 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -2107,7 +2107,7 @@ function baseCreateRenderer( } if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) { - ;(parentComponent!.ctx as KeepAliveContext).deactivate(vnode) + ;(vnode.component!.parent!.ctx as KeepAliveContext).deactivate(vnode) return } From 5a77b2a0aae3375985a6e6eae11593cb4a2a5eab Mon Sep 17 00:00:00 2001 From: daiwei Date: Thu, 22 May 2025 11:17:17 +0800 Subject: [PATCH 2/2] chore: track KeepAlive child as block inside v-for --- packages/compiler-core/src/transforms/vFor.ts | 5 ++++- .../__tests__/rendererOptimizedMode.spec.ts | 13 ++++++++----- packages/runtime-core/src/renderer.ts | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/compiler-core/src/transforms/vFor.ts b/packages/compiler-core/src/transforms/vFor.ts index 0dca0ba9ab4..8aba1f2c1b2 100644 --- a/packages/compiler-core/src/transforms/vFor.ts +++ b/packages/compiler-core/src/transforms/vFor.ts @@ -42,6 +42,7 @@ import { import { FRAGMENT, IS_MEMO_SAME, + KEEP_ALIVE, OPEN_BLOCK, RENDER_LIST, } from '../runtimeHelpers' @@ -208,7 +209,9 @@ export const transformFor: NodeTransform = createStructuralDirectiveTransform( ) } } - childBlock.isBlock = !isStableFragment + // track KeepAlive child as block + childBlock.isBlock = + childBlock.tag === KEEP_ALIVE || !isStableFragment if (childBlock.isBlock) { helper(OPEN_BLOCK) helper(getVNodeBlockHelper(context.inSSR, childBlock.isComponent)) diff --git a/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts b/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts index 7587ecfcbaa..f6fd16692c4 100644 --- a/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts +++ b/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts @@ -1431,11 +1431,14 @@ describe('renderer: optimized mode', () => { Fragment, null, renderList(1, () => { - return createVNode( - KeepAlive, - null, - [(openBlock(), createBlock(view.value))], - 1024 /* DYNAMIC_SLOTS */, + return ( + openBlock(), + createBlock( + KeepAlive, + { include: [] }, + [(openBlock(), createBlock(view.value))], + 1024 /* DYNAMIC_SLOTS */, + ) ) }), 64 /* STABLE_FRAGMENT */, diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 23c0f447d1e..7b39aa917a2 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -2110,7 +2110,7 @@ function baseCreateRenderer( } if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) { - ;(vnode.component!.parent!.ctx as KeepAliveContext).deactivate(vnode) + ;(parentComponent!.ctx as KeepAliveContext).deactivate(vnode) return }