diff --git a/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts b/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts index bddd7d1d318..59deb5a00e2 100644 --- a/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts +++ b/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts @@ -20,7 +20,9 @@ import { onBeforeUnmount, createTextVNode, SetupContext, - createApp + createApp, + FunctionalComponent, + renderList } from '@vue/runtime-test' import { PatchFlags, SlotFlags } from '@vue/shared' import { SuspenseImpl } from '../src/components/Suspense' @@ -821,4 +823,62 @@ describe('renderer: optimized mode', () => { await nextTick() expect(inner(root)).toBe('
true
') }) + + // #3881 + // root cause: fragment inside a compiled slot passed to component which + // programmatically invokes the slot. The entire slot should de-opt but + // the fragment was incorretly put in optimized mode which causes it to skip + // updates for its inner components. + test('fragments inside programmatically invoked compiled slot should de-opt properly', async () => { + const Parent: FunctionalComponent = (_, { slots }) => slots.default!() + const Dummy = () => 'dummy' + + const toggle = ref(true) + const force = ref(0) + + const app = createApp({ + render() { + if (!toggle.value) { + return null + } + return h( + Parent, + { n: force.value }, + { + default: withCtx( + () => [ + createVNode('ul', null, [ + (openBlock(), + createBlock( + Fragment, + null, + renderList(1, item => { + return createVNode('li', null, [createVNode(Dummy)]) + }), + 64 /* STABLE_FRAGMENT */ + )) + ]) + ], + undefined, + true + ), + _: 1 /* STABLE */ + } + ) + } + }) + + app.mount(root) + + // force a patch + force.value++ + await nextTick() + expect(inner(root)).toBe(``) + + // unmount + toggle.value = false + await nextTick() + // should successfully unmount without error + expect(inner(root)).toBe(``) + }) }) diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index e190669951c..c7549ae1598 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -1170,7 +1170,7 @@ function baseCreateRenderer( const fragmentEndAnchor = (n2.anchor = n1 ? n1.anchor : hostCreateText(''))! let { patchFlag, dynamicChildren, slotScopeIds: fragmentSlotScopeIds } = n2 - if (patchFlag > 0) { + if (dynamicChildren) { optimized = true }