From 140a7681cc3bba22f55d97fd85a5eafe97a1230f Mon Sep 17 00:00:00 2001 From: edison Date: Sun, 14 Apr 2024 23:16:01 +0800 Subject: [PATCH] fix(TransitionGroup): avoid set transition hooks for comment nodes and text nodes (#9421) close #4621 close #4622 close #5153 close #5168 close #7898 close #9067 --- .../src/components/TransitionGroup.ts | 35 +++-- .../vue/__tests__/e2e/TransitionGroup.spec.ts | 122 ++++++++++++++++++ 2 files changed, 145 insertions(+), 12 deletions(-) diff --git a/packages/runtime-dom/src/components/TransitionGroup.ts b/packages/runtime-dom/src/components/TransitionGroup.ts index f98e82b2734..763b7a98b24 100644 --- a/packages/runtime-dom/src/components/TransitionGroup.ts +++ b/packages/runtime-dom/src/components/TransitionGroup.ts @@ -112,7 +112,29 @@ const TransitionGroupImpl: ComponentOptions = { tag = 'span' } - prevChildren = children + prevChildren = [] + if (children) { + for (let i = 0; i < children.length; i++) { + const child = children[i] + if (child.el && child.el instanceof Element) { + prevChildren.push(child) + setTransitionHooks( + child, + resolveTransitionHooks( + child, + cssTransitionProps, + state, + instance, + ), + ) + positionMap.set( + child, + (child.el as Element).getBoundingClientRect(), + ) + } + } + } + children = slots.default ? getTransitionRawChildren(slots.default()) : [] for (let i = 0; i < children.length; i++) { @@ -127,17 +149,6 @@ const TransitionGroupImpl: ComponentOptions = { } } - if (prevChildren) { - for (let i = 0; i < prevChildren.length; i++) { - const child = prevChildren[i] - setTransitionHooks( - child, - resolveTransitionHooks(child, cssTransitionProps, state, instance), - ) - positionMap.set(child, (child.el as Element).getBoundingClientRect()) - } - } - return createVNode(tag, null, children) } }, diff --git a/packages/vue/__tests__/e2e/TransitionGroup.spec.ts b/packages/vue/__tests__/e2e/TransitionGroup.spec.ts index febc9d3c20a..da3f4a42de9 100644 --- a/packages/vue/__tests__/e2e/TransitionGroup.spec.ts +++ b/packages/vue/__tests__/e2e/TransitionGroup.spec.ts @@ -508,4 +508,126 @@ describe('e2e: TransitionGroup', () => { expect(` children must be keyed`).toHaveBeenWarned() }) + + // #5168, #7898, #9067 + test( + 'avoid set transition hooks for comment node', + async () => { + await page().evaluate(duration => { + const { createApp, ref, h, createCommentVNode } = (window as any).Vue + + const show = ref(false) + createApp({ + template: ` +
+ +
{{item}}
+ +
+
+ + `, + components: { + Child: { + setup() { + return () => + show.value + ? h('div', { class: 'test' }, 'child') + : createCommentVNode('v-if', true) + }, + }, + }, + setup: () => { + const items = ref([]) + const click = () => { + items.value = ['a', 'b', 'c'] + setTimeout(() => { + show.value = true + }, duration) + } + return { click, items } + }, + }).mount('#app') + }, duration) + + expect(await html('#container')).toBe(``) + + expect(await htmlWhenTransitionStart()).toBe( + `
a
` + + `
b
` + + `
c
` + + ``, + ) + + await transitionFinish(duration) + await nextFrame() + expect(await html('#container')).toBe( + `
a
` + + `
b
` + + `
c
` + + `
child
`, + ) + + await transitionFinish(duration) + expect(await html('#container')).toBe( + `
a
` + + `
b
` + + `
c
` + + `
child
`, + ) + }, + E2E_TIMEOUT, + ) + + // #4621, #4622, #5153 + test( + 'avoid set transition hooks for text node', + async () => { + await page().evaluate(() => { + const { createApp, ref } = (window as any).Vue + const app = createApp({ + template: ` +
+ +
foo
+
bar
+
+
+ + `, + setup: () => { + const show = ref(false) + const click = () => { + show.value = true + } + return { show, click } + }, + }) + + app.config.compilerOptions.whitespace = 'preserve' + app.mount('#app') + }) + + expect(await html('#container')).toBe(`
foo
` + ` `) + + expect(await htmlWhenTransitionStart()).toBe( + `
foo
` + + ` ` + + `
bar
`, + ) + + await nextFrame() + expect(await html('#container')).toBe( + `
foo
` + + ` ` + + `
bar
`, + ) + + await transitionFinish(duration) + expect(await html('#container')).toBe( + `
foo
` + ` ` + `
bar
`, + ) + }, + E2E_TIMEOUT, + ) })