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 1d8bb84205b..f6fd16692c4 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, @@ -1402,4 +1404,54 @@ 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 ( + openBlock(), + createBlock( + KeepAlive, + { include: [] }, + [(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') + }) })