diff --git a/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts b/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts index c2073b966a7..2dbe87e6c43 100644 --- a/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts +++ b/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts @@ -23,7 +23,9 @@ import { createApp, FunctionalComponent, renderList, - onUnmounted + onUnmounted, + createElementBlock, + createElementVNode } from '@vue/runtime-test' import { PatchFlags, SlotFlags } from '@vue/shared' import { SuspenseImpl } from '../src/components/Suspense' @@ -697,6 +699,81 @@ describe('renderer: optimized mode', () => { ) }) + test('force full diff slot and fallback nodes', async () => { + const Comp = { + props: ['show'], + setup(props: any, { slots }: SetupContext) { + return () => { + return ( + openBlock(), + createElementBlock('div', null, [ + renderSlot(slots, 'default', { hide: !props.show }, () => [ + true + ? (openBlock(), + (block = createElementBlock( + Fragment, + { key: 0 }, + [createTextVNode('foo')], + PatchFlags.STABLE_FRAGMENT + ))) + : createCommentVNode('v-if', true) + ]) + ]) + ) + } + } + } + + 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
') + }) + // 3569 test('should force bailout when the user manually calls the slot function', async () => { const index = ref(0) diff --git a/packages/runtime-core/src/helpers/renderSlot.ts b/packages/runtime-core/src/helpers/renderSlot.ts index 452fc827269..f5d8ca4041e 100644 --- a/packages/runtime-core/src/helpers/renderSlot.ts +++ b/packages/runtime-core/src/helpers/renderSlot.ts @@ -38,7 +38,14 @@ export function renderSlot( currentRenderingInstance!.parent.isCE) ) { if (name !== 'default') props.name = name - return createVNode('slot', props, fallback && fallback()) + let renderFallback = false + const node = createVNode( + 'slot', + props, + fallback && ((renderFallback = true), fallback()) + ) + if (renderFallback) node.patchFlag = PatchFlags.BAIL + return node } let slot = slots[name] @@ -59,6 +66,7 @@ export function renderSlot( if (slot && (slot as ContextualRenderFn)._c) { ;(slot as ContextualRenderFn)._d = false } + let renderFallback = false openBlock() const validSlotContent = slot && ensureValidVNode(slot(props)) const rendered = createBlock( @@ -71,7 +79,7 @@ export function renderSlot( (validSlotContent && (validSlotContent as any).key) || `_${name}` }, - validSlotContent || (fallback ? fallback() : []), + validSlotContent || (fallback ? ((renderFallback = true), fallback()) : []), validSlotContent && (slots as RawSlots)._ === SlotFlags.STABLE ? PatchFlags.STABLE_FRAGMENT : PatchFlags.BAIL @@ -82,6 +90,7 @@ export function renderSlot( if (slot && (slot as ContextualRenderFn)._c) { ;(slot as ContextualRenderFn)._d = true } + if (renderFallback) rendered.patchFlag = PatchFlags.BAIL return rendered } diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 383e17fb0f5..7ed3f1f7a74 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -825,7 +825,7 @@ function baseCreateRenderer( } const areChildrenSVG = isSVG && n2.type !== 'foreignObject' - if (dynamicChildren) { + if (optimized && dynamicChildren) { patchBlockChildren( n1.dynamicChildren!, dynamicChildren, @@ -1058,9 +1058,10 @@ function baseCreateRenderer( let { patchFlag, dynamicChildren, slotScopeIds: fragmentSlotScopeIds } = n2 if ( - __DEV__ && - // #5523 dev root fragment may inherit directives - (isHmrUpdating || patchFlag & PatchFlags.DEV_ROOT_FRAGMENT) + (__DEV__ && + // #5523 dev root fragment may inherit directives + (isHmrUpdating || patchFlag & PatchFlags.DEV_ROOT_FRAGMENT)) || + (n1 || n2).patchFlag === PatchFlags.BAIL ) { // HMR updated / Dev root fragment (w/ comments), force full diff patchFlag = 0 @@ -1093,6 +1094,7 @@ function baseCreateRenderer( ) } else { if ( + optimized && patchFlag > 0 && patchFlag & PatchFlags.STABLE_FRAGMENT && dynamicChildren &&