From d629c5dd68f1d4e552adfbeae3c5fc13bee9f56d Mon Sep 17 00:00:00 2001 From: qmhc <40221744+qmhc@users.noreply.github.com> Date: Thu, 7 Jul 2022 12:15:52 +0800 Subject: [PATCH 1/5] fix: comments before single root element breaking Transition --- packages/runtime-core/src/renderer.ts | 37 +++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 198a4c8f4ff..1f9f885e161 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -2168,7 +2168,9 @@ function baseCreateRenderer( const remove: RemoveFn = vnode => { const { type, el, anchor, transition } = vnode - if (type === Fragment) { + const isFragment = type === Fragment + + if (!transition && isFragment) { removeFragment(el!, anchor!) return } @@ -2179,19 +2181,20 @@ 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 && transition && - !transition.persisted + !transition.persisted && + (vnode.shapeFlag & ShapeFlags.ELEMENT || isFragment) ) { const { leave, delayLeave } = transition - const performLeave = () => leave(el!, performRemove) + const effectiveEl = isFragment ? getFirstElement(el!, anchor!) : el! + const performLeave = () => leave(effectiveEl, performRemove) if (delayLeave) { delayLeave(vnode.el!, performRemove, performLeave) } else { @@ -2200,6 +2203,22 @@ function baseCreateRenderer( } else { performRemove() } + + // if ( + // vnode.shapeFlag & ShapeFlags.ELEMENT && + // transition && + // !transition.persisted + // ) { + // const { leave, delayLeave } = transition + // const performLeave = () => leave(el!, performRemove) + // if (delayLeave) { + // delayLeave(vnode.el!, performRemove, performLeave) + // } else { + // performLeave() + // } + // } else { + // performRemove() + // } } const removeFragment = (cur: RendererNode, end: RendererNode) => { @@ -2214,6 +2233,14 @@ function baseCreateRenderer( hostRemove(end) } + const getFirstElement = (cur: RendererNode, end: RendererNode) => { + while (cur.nodeType !== Node.ELEMENT_NODE && cur !== end) { + cur = hostNextSibling(cur)! + } + + return cur + } + const unmountComponent = ( instance: ComponentInternalInstance, parentSuspense: SuspenseBoundary | null, From 3e404253a1a01292544a512a6e2f28d29bdf91b0 Mon Sep 17 00:00:00 2001 From: qmhc <40221744+qmhc@users.noreply.github.com> Date: Thu, 7 Jul 2022 12:23:36 +0800 Subject: [PATCH 2/5] chore: remove comments --- packages/runtime-core/src/renderer.ts | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 1f9f885e161..e92eff58474 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -2203,22 +2203,6 @@ function baseCreateRenderer( } else { performRemove() } - - // if ( - // vnode.shapeFlag & ShapeFlags.ELEMENT && - // transition && - // !transition.persisted - // ) { - // const { leave, delayLeave } = transition - // const performLeave = () => leave(el!, performRemove) - // if (delayLeave) { - // delayLeave(vnode.el!, performRemove, performLeave) - // } else { - // performLeave() - // } - // } else { - // performRemove() - // } } const removeFragment = (cur: RendererNode, end: RendererNode) => { From 9805a64b1c827c0b761c691981c08b4943a158d6 Mon Sep 17 00:00:00 2001 From: qmhc <40221744+qmhc@users.noreply.github.com> Date: Thu, 7 Jul 2022 14:52:10 +0800 Subject: [PATCH 3/5] test: add e2e test case --- packages/vue/__tests__/Transition.spec.ts | 79 +++++++++++++++++++++-- 1 file changed, 75 insertions(+), 4 deletions(-) diff --git a/packages/vue/__tests__/Transition.spec.ts b/packages/vue/__tests__/Transition.spec.ts index 61926310d29..78b7eb9e122 100644 --- a/packages/vue/__tests__/Transition.spec.ts +++ b/packages/vue/__tests__/Transition.spec.ts @@ -2123,7 +2123,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 @@ -2131,9 +2131,9 @@ describe('e2e: Transition', () => { components: { Comp: { template: ` - -
- ` + +
+ ` } }, template: ` @@ -2191,4 +2191,75 @@ describe('e2e: Transition', () => { }, E2E_TIMEOUT ) + + test( + 'toggle multiple components with comments before the single root element', + async () => { + await page().evaluate(() => { + const { createApp, ref } = (window as any).Vue + createApp({ + components: { + One: { + template: ` + +
One
+ ` + }, + Two: { + template: `
Two
` + } + }, + template: ` + +
+ + + + +
+ `, + setup: () => { + const toggle = ref(true) + const click = () => (toggle.value = !toggle.value) + return { toggle, click } + } + }).mount('#app') + }) + + expect(await html('#container')).toBe( + '
One
' + ) + + // one -> two + expect(await classWhenTransitionStart()).toStrictEqual([ + 'one', + 'v-leave-from', + 'v-leave-active' + ]) + await nextFrame() + expect(await classList('.two')).toStrictEqual([ + 'two', + 'v-enter-from', + 'v-enter-active' + ]) + await transitionFinish(duration * 2) + expect(await html('#container')).toBe('
Two
') + + // two -> one + expect(await classWhenTransitionStart()).toStrictEqual([ + 'two', + 'v-leave-from', + 'v-leave-active' + ]) + await nextFrame() + expect(await classList('.one')).toStrictEqual([ + 'one', + 'v-enter-from', + 'v-enter-active' + ]) + await transitionFinish(duration * 2) + expect(await html('#container')).toBe( + '
One
' + ) + }, E2E_TIMEOUT) }) From 0fb20565b295c03dd7f5a26523e25a46ca762fb1 Mon Sep 17 00:00:00 2001 From: qmhc <40221744+qmhc@users.noreply.github.com> Date: Wed, 4 Jan 2023 10:30:03 +0800 Subject: [PATCH 4/5] chore: distinguish dev mode --- packages/runtime-core/src/renderer.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 54d4178fe1c..e06cd04e8a6 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -2155,7 +2155,7 @@ function baseCreateRenderer( const { type, el, anchor, transition } = vnode const isFragment = type === Fragment - if (!transition && isFragment) { + if ((!__DEV__ || !transition) && isFragment) { removeFragment(el!, anchor!) return } @@ -2173,15 +2173,16 @@ function baseCreateRenderer( } if ( + (isFragment || vnode.shapeFlag & ShapeFlags.ELEMENT) && transition && - !transition.persisted && - (vnode.shapeFlag & ShapeFlags.ELEMENT || isFragment) + !transition.persisted ) { const { leave, delayLeave } = transition - const effectiveEl = isFragment ? getFirstElement(el!, anchor!) : el! + 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() } From 8cd3e33a6effda464f60417b2491babee45ef2f7 Mon Sep 17 00:00:00 2001 From: qmhc <544022268@qq.com> Date: Fri, 30 Aug 2024 00:30:16 +0800 Subject: [PATCH 5/5] chore: replace to DOMNodeTypes.ELEMENT --- packages/runtime-core/src/renderer.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 901e1beca95..7d09a732daf 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 { @@ -2230,7 +2234,7 @@ function baseCreateRenderer( } const getFirstElement = (cur: RendererNode, end: RendererNode) => { - while (cur.nodeType !== Node.ELEMENT_NODE && cur !== end) { + while (cur.nodeType !== DOMNodeTypes.ELEMENT && cur !== end) { cur = hostNextSibling(cur)! }