diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 90cc22f5470..cb68d558d5e 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -72,7 +72,11 @@ import { } from './components/Teleport' import { type KeepAliveContext, isKeepAlive } from './components/KeepAlive' import { isHmrUpdating, registerHMR, unregisterHMR } from './hmr' -import { type RootHydrateFunction, createHydrationFunctions } from './hydration' +import { + DOMNodeTypes, + type RootHydrateFunction, + createHydrationFunctions, +} from './hydration' import { invokeDirectiveHook } from './directives' import { endMeasure, startMeasure } from './profiling' import { @@ -2185,24 +2189,10 @@ function baseCreateRenderer( const remove: RemoveFn = vnode => { const { type, el, anchor, transition } = vnode - if (type === Fragment) { - if ( - __DEV__ && - vnode.patchFlag > 0 && - vnode.patchFlag & PatchFlags.DEV_ROOT_FRAGMENT && - transition && - !transition.persisted - ) { - ;(vnode.children as VNode[]).forEach(child => { - if (child.type === Comment) { - hostRemove(child.el!) - } else { - remove(child) - } - }) - } else { - removeFragment(el!, anchor!) - } + const isFragment = type === Fragment + + if ((!__DEV__ || !transition) && isFragment) { + removeFragment(el!, anchor!) return } @@ -2212,21 +2202,23 @@ function baseCreateRenderer( } const performRemove = () => { - hostRemove(el!) + isFragment ? removeFragment(el!, anchor!) : hostRemove(el!) if (transition && !transition.persisted && transition.afterLeave) { transition.afterLeave() } } if ( - vnode.shapeFlag & ShapeFlags.ELEMENT && + (isFragment || vnode.shapeFlag & ShapeFlags.ELEMENT) && transition && !transition.persisted ) { const { leave, delayLeave } = transition - const performLeave = () => leave(el!, performRemove) + const effectiveEl = + __DEV__ && !isFragment ? el! : getFirstElement(el!, anchor!) + const performLeave = () => leave(effectiveEl, performRemove) if (delayLeave) { - delayLeave(vnode.el!, performRemove, performLeave) + delayLeave(el!, performRemove, performLeave) } else { performLeave() } @@ -2247,6 +2239,14 @@ function baseCreateRenderer( hostRemove(end) } + const getFirstElement = (cur: RendererNode, end: RendererNode) => { + while (cur.nodeType !== DOMNodeTypes.ELEMENT && cur !== end) { + cur = hostNextSibling(cur)! + } + + return cur + } + const unmountComponent = ( instance: ComponentInternalInstance, parentSuspense: SuspenseBoundary | null, diff --git a/packages/vue/__tests__/e2e/Transition.spec.ts b/packages/vue/__tests__/e2e/Transition.spec.ts index c0863a75991..869dc4694d5 100644 --- a/packages/vue/__tests__/e2e/Transition.spec.ts +++ b/packages/vue/__tests__/e2e/Transition.spec.ts @@ -3053,7 +3053,7 @@ describe('e2e: Transition', () => { }) test( - 'should work with dev root fragment', + 'toggle single component with comments before the single root element', async () => { await page().evaluate(() => { const { createApp, ref } = (window as any).Vue @@ -3061,9 +3061,9 @@ describe('e2e: Transition', () => { components: { Comp: { template: ` - -