diff --git a/packages/runtime-core/__tests__/helpers/renderSlot.spec.ts b/packages/runtime-core/__tests__/helpers/renderSlot.spec.ts index b6183ac3c..c4ae077ba 100644 --- a/packages/runtime-core/__tests__/helpers/renderSlot.spec.ts +++ b/packages/runtime-core/__tests__/helpers/renderSlot.spec.ts @@ -26,13 +26,17 @@ describe('renderSlot', () => { const vnode = renderSlot( { default: () => [(child = h('child'))] }, 'default', + { key: 'foo' }, ) expect(vnode.children).toEqual([child]) + expect(vnode.key).toBe('foo') }) it('should render slot fallback', () => { - const vnode = renderSlot({}, 'default', {}, () => ['fallback']) + const vnode = renderSlot({}, 'default', { key: 'foo' }, () => ['fallback']) expect(vnode.children).toEqual(['fallback']) + // should attach fallback key postfix + expect(vnode.key).toBe('foo_fb') }) it('should warn render ssr slot', () => { diff --git a/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts b/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts index dceda28fc..7abd94e19 100644 --- a/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts +++ b/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts @@ -1105,4 +1105,77 @@ describe('renderer: optimized mode', () => { expect(app.config.errorHandler).not.toHaveBeenCalled() } }) + + test('diff slot and slot fallback node', async () => { + const Comp = { + props: ['show'], + setup(props: any, { slots }: SetupContext) { + return () => { + return ( + openBlock(), + createElementBlock('div', null, [ + renderSlot(slots, 'default', { hide: !props.show }, () => [ + (openBlock(), + (block = createElementBlock( + Fragment, + { key: 0 }, + [createTextVNode('foo')], + PatchFlags.STABLE_FRAGMENT, + ))), + ]), + ]) + ) + } + }, + } + + const show = ref(true) + const app = createApp({ + render() { + return ( + openBlock(), + createBlock( + Comp, + { show: show.value }, + { + default: withCtx(({ hide }: { hide: boolean }) => [ + !hide + ? (openBlock(), + createElementBlock( + Fragment, + { key: 0 }, + [ + createCommentVNode('comment'), + createElementVNode( + 'div', + null, + 'bar', + PatchFlags.HOISTED, + ), + ], + PatchFlags.STABLE_FRAGMENT, + )) + : createCommentVNode('v-if', true), + ]), + _: SlotFlags.STABLE, + }, + PatchFlags.PROPS, + ['show'], + ) + ) + }, + }) + + app.mount(root) + expect(inner(root)).toBe('
bar
') + expect(block).toBe(null) + + show.value = false + await nextTick() + expect(inner(root)).toBe('
foo
') + + show.value = true + await nextTick() + expect(inner(root)).toBe('
bar
') + }) }) diff --git a/packages/runtime-core/src/helpers/renderSlot.ts b/packages/runtime-core/src/helpers/renderSlot.ts index 8a7608219..f0b13904f 100644 --- a/packages/runtime-core/src/helpers/renderSlot.ts +++ b/packages/runtime-core/src/helpers/renderSlot.ts @@ -65,11 +65,13 @@ export function renderSlot( Fragment, { key: - props.key || - // slot content array of a dynamic conditional slot may have a branch - // key attached in the `createSlots` helper, respect that - (validSlotContent && (validSlotContent as any).key) || - `_${name}`, + (props.key || + // slot content array of a dynamic conditional slot may have a branch + // key attached in the `createSlots` helper, respect that + (validSlotContent && (validSlotContent as any).key) || + `_${name}`) + + // #7256 force differentiate fallback content from actual content + (!validSlotContent && fallback ? '_fb' : ''), }, validSlotContent || (fallback ? fallback() : []), validSlotContent && (slots as RawSlots)._ === SlotFlags.STABLE