From 1afa3adad47e88fbe6f792145b1b8ddf48ac4a44 Mon Sep 17 00:00:00 2001 From: daiwei Date: Wed, 18 Oct 2023 16:08:39 +0800 Subject: [PATCH 01/11] fix(TransitionGroup): avoid set transition hooks for comment node --- packages/runtime-core/src/hydration.ts | 2 +- packages/runtime-core/src/index.ts | 1 + packages/runtime-dom/src/components/TransitionGroup.ts | 6 +++++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/runtime-core/src/hydration.ts b/packages/runtime-core/src/hydration.ts index 89a00886332..8f156e712d4 100644 --- a/packages/runtime-core/src/hydration.ts +++ b/packages/runtime-core/src/hydration.ts @@ -41,7 +41,7 @@ let hasMismatch = false const isSVGContainer = (container: Element) => /svg/.test(container.namespaceURI!) && container.tagName !== 'foreignObject' -const isComment = (node: Node): node is Comment => +export const isComment = (node: Node): node is Comment => node.nodeType === DOMNodeTypes.COMMENT // Note: hydration is DOM-specific diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index 98aee757dab..b8ebacad0f0 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -254,6 +254,7 @@ export type { RootRenderFunction } from './renderer' export type { RootHydrateFunction } from './hydration' +export { isComment } from './hydration' export type { Slot, Slots, SlotsType } from './componentSlots' export type { Prop, diff --git a/packages/runtime-dom/src/components/TransitionGroup.ts b/packages/runtime-dom/src/components/TransitionGroup.ts index fc5d260b91e..2a2d0f16ee4 100644 --- a/packages/runtime-dom/src/components/TransitionGroup.ts +++ b/packages/runtime-dom/src/components/TransitionGroup.ts @@ -24,7 +24,8 @@ import { toRaw, compatUtils, DeprecationTypes, - ComponentOptions + ComponentOptions, + isComment } from '@vue/runtime-core' import { extend } from '@vue/shared' @@ -128,6 +129,9 @@ const TransitionGroupImpl: ComponentOptions = { } if (prevChildren) { + prevChildren = prevChildren.filter( + child => !isComment(child.el as Element) + ) for (let i = 0; i < prevChildren.length; i++) { const child = prevChildren[i] setTransitionHooks( From 074afe684872d977815c3d8fccbd986fc1ad3529 Mon Sep 17 00:00:00 2001 From: edison1105 Date: Tue, 24 Oct 2023 21:29:35 +0800 Subject: [PATCH 02/11] test: add test case --- .../vue/__tests__/e2e/TransitionGroup.spec.ts | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/packages/vue/__tests__/e2e/TransitionGroup.spec.ts b/packages/vue/__tests__/e2e/TransitionGroup.spec.ts index 1a61c23a44e..41be978d135 100644 --- a/packages/vue/__tests__/e2e/TransitionGroup.spec.ts +++ b/packages/vue/__tests__/e2e/TransitionGroup.spec.ts @@ -508,4 +508,81 @@ describe('e2e: TransitionGroup', () => { expect(` children must be keyed`).toHaveBeenWarned() }) + + // #5168 + 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) + expect(await html('#container')).toBe( + `
a
` + + `
b
` + + `
c
` + + `
child
` + ) + + 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 + ) }) From 3944b754ed3068bfd1488e753ced49c9b9466192 Mon Sep 17 00:00:00 2001 From: edison1105 Date: Tue, 24 Oct 2023 21:34:19 +0800 Subject: [PATCH 03/11] test: add test case --- .../vue/__tests__/e2e/TransitionGroup.spec.ts | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/packages/vue/__tests__/e2e/TransitionGroup.spec.ts b/packages/vue/__tests__/e2e/TransitionGroup.spec.ts index 41be978d135..ee3ce0fd350 100644 --- a/packages/vue/__tests__/e2e/TransitionGroup.spec.ts +++ b/packages/vue/__tests__/e2e/TransitionGroup.spec.ts @@ -519,13 +519,13 @@ describe('e2e: TransitionGroup', () => { const show = ref(false) createApp({ template: ` -
- -
{{item}}
- -
-
- +
+ +
{{item}}
+ +
+
+ `, components: { Child: { @@ -560,13 +560,6 @@ describe('e2e: TransitionGroup', () => { ) await transitionFinish(duration) - expect(await html('#container')).toBe( - `
a
` + - `
b
` + - `
c
` + - `
child
` - ) - await nextFrame() expect(await html('#container')).toBe( `
a
` + From 9acce54322e8ef423874c03c65ff438dc41d385f Mon Sep 17 00:00:00 2001 From: edison1105 Date: Tue, 24 Oct 2023 21:48:34 +0800 Subject: [PATCH 04/11] chore: improve code --- packages/runtime-dom/src/components/TransitionGroup.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/runtime-dom/src/components/TransitionGroup.ts b/packages/runtime-dom/src/components/TransitionGroup.ts index 2a2d0f16ee4..4709c56ee55 100644 --- a/packages/runtime-dom/src/components/TransitionGroup.ts +++ b/packages/runtime-dom/src/components/TransitionGroup.ts @@ -113,7 +113,8 @@ const TransitionGroupImpl: ComponentOptions = { tag = 'span' } - prevChildren = children + prevChildren = + children && children.filter(child => !isComment(child.el as Element)) children = slots.default ? getTransitionRawChildren(slots.default()) : [] for (let i = 0; i < children.length; i++) { @@ -129,9 +130,6 @@ const TransitionGroupImpl: ComponentOptions = { } if (prevChildren) { - prevChildren = prevChildren.filter( - child => !isComment(child.el as Element) - ) for (let i = 0; i < prevChildren.length; i++) { const child = prevChildren[i] setTransitionHooks( From e31e02d61c1eca5a418a73ec1dedab34dc84dd9e Mon Sep 17 00:00:00 2001 From: edison1105 Date: Tue, 24 Oct 2023 21:53:24 +0800 Subject: [PATCH 05/11] chore: improve code --- packages/vue/__tests__/e2e/TransitionGroup.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vue/__tests__/e2e/TransitionGroup.spec.ts b/packages/vue/__tests__/e2e/TransitionGroup.spec.ts index ee3ce0fd350..9636b55069a 100644 --- a/packages/vue/__tests__/e2e/TransitionGroup.spec.ts +++ b/packages/vue/__tests__/e2e/TransitionGroup.spec.ts @@ -526,7 +526,7 @@ describe('e2e: TransitionGroup', () => { - `, + `, components: { Child: { setup() { From 9f34512e58bf2f8b13bcf3769a68123a02b2ebb7 Mon Sep 17 00:00:00 2001 From: daiwei Date: Wed, 25 Oct 2023 08:36:41 +0800 Subject: [PATCH 06/11] chore: improve code --- packages/runtime-core/src/index.ts | 1 - packages/runtime-dom/src/components/TransitionGroup.ts | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index b8ebacad0f0..98aee757dab 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -254,7 +254,6 @@ export type { RootRenderFunction } from './renderer' export type { RootHydrateFunction } from './hydration' -export { isComment } from './hydration' export type { Slot, Slots, SlotsType } from './componentSlots' export type { Prop, diff --git a/packages/runtime-dom/src/components/TransitionGroup.ts b/packages/runtime-dom/src/components/TransitionGroup.ts index 4709c56ee55..f948d387239 100644 --- a/packages/runtime-dom/src/components/TransitionGroup.ts +++ b/packages/runtime-dom/src/components/TransitionGroup.ts @@ -24,9 +24,9 @@ import { toRaw, compatUtils, DeprecationTypes, - ComponentOptions, - isComment + ComponentOptions } from '@vue/runtime-core' +import { isComment } from '../../../runtime-core/src/hydration' import { extend } from '@vue/shared' const positionMap = new WeakMap() From 4ec36dc6d30af02d1ef77aa04ba5f00238953246 Mon Sep 17 00:00:00 2001 From: daiwei Date: Wed, 25 Oct 2023 17:44:09 +0800 Subject: [PATCH 07/11] chore: improve code --- packages/runtime-core/src/hydration.ts | 2 +- packages/runtime-dom/src/components/TransitionGroup.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/runtime-core/src/hydration.ts b/packages/runtime-core/src/hydration.ts index c1bcd32a3f1..4e91cb3d1cb 100644 --- a/packages/runtime-core/src/hydration.ts +++ b/packages/runtime-core/src/hydration.ts @@ -41,7 +41,7 @@ let hasMismatch = false const isSVGContainer = (container: Element) => /svg/.test(container.namespaceURI!) && container.tagName !== 'foreignObject' -export const isComment = (node: Node): node is Comment => +const isComment = (node: Node): node is Comment => node.nodeType === DOMNodeTypes.COMMENT // Note: hydration is DOM-specific diff --git a/packages/runtime-dom/src/components/TransitionGroup.ts b/packages/runtime-dom/src/components/TransitionGroup.ts index f948d387239..cc396bdc62d 100644 --- a/packages/runtime-dom/src/components/TransitionGroup.ts +++ b/packages/runtime-dom/src/components/TransitionGroup.ts @@ -26,7 +26,6 @@ import { DeprecationTypes, ComponentOptions } from '@vue/runtime-core' -import { isComment } from '../../../runtime-core/src/hydration' import { extend } from '@vue/shared' const positionMap = new WeakMap() @@ -114,7 +113,8 @@ const TransitionGroupImpl: ComponentOptions = { } prevChildren = - children && children.filter(child => !isComment(child.el as Element)) + children && + children.filter(child => child.el && child.el instanceof Element) children = slots.default ? getTransitionRawChildren(slots.default()) : [] for (let i = 0; i < children.length; i++) { From ed1e8a5b374dda9fc219f29c3a286ecc762090b7 Mon Sep 17 00:00:00 2001 From: edison1105 Date: Wed, 25 Oct 2023 20:54:42 +0800 Subject: [PATCH 08/11] test: add more test case --- .../vue/__tests__/e2e/TransitionGroup.spec.ts | 54 ++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/packages/vue/__tests__/e2e/TransitionGroup.spec.ts b/packages/vue/__tests__/e2e/TransitionGroup.spec.ts index 9636b55069a..1bb1fdd3753 100644 --- a/packages/vue/__tests__/e2e/TransitionGroup.spec.ts +++ b/packages/vue/__tests__/e2e/TransitionGroup.spec.ts @@ -509,7 +509,7 @@ describe('e2e: TransitionGroup', () => { expect(` children must be keyed`).toHaveBeenWarned() }) - // #5168 + // #5168,#7898,#9067 test( 'avoid set transition hooks for comment node', async () => { @@ -578,4 +578,56 @@ describe('e2e: TransitionGroup', () => { }, 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 + ) }) From 3e28ec36e3a27086d6b5bd4a89426a6ccc76711e Mon Sep 17 00:00:00 2001 From: edison1105 Date: Wed, 25 Oct 2023 20:58:55 +0800 Subject: [PATCH 09/11] chore: add comments --- packages/runtime-dom/src/components/TransitionGroup.ts | 1 + packages/vue/__tests__/e2e/TransitionGroup.spec.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/runtime-dom/src/components/TransitionGroup.ts b/packages/runtime-dom/src/components/TransitionGroup.ts index cc396bdc62d..95b3a060125 100644 --- a/packages/runtime-dom/src/components/TransitionGroup.ts +++ b/packages/runtime-dom/src/components/TransitionGroup.ts @@ -112,6 +112,7 @@ const TransitionGroupImpl: ComponentOptions = { tag = 'span' } + // filter out comment nodes and text nodes prevChildren = children && children.filter(child => child.el && child.el instanceof Element) diff --git a/packages/vue/__tests__/e2e/TransitionGroup.spec.ts b/packages/vue/__tests__/e2e/TransitionGroup.spec.ts index 1bb1fdd3753..950d9ab98a1 100644 --- a/packages/vue/__tests__/e2e/TransitionGroup.spec.ts +++ b/packages/vue/__tests__/e2e/TransitionGroup.spec.ts @@ -509,7 +509,7 @@ describe('e2e: TransitionGroup', () => { expect(` children must be keyed`).toHaveBeenWarned() }) - // #5168,#7898,#9067 + // #5168, #7898, #9067 test( 'avoid set transition hooks for comment node', async () => { From a2c3bac5ab831f045571f84c9a04e6d909d79636 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sun, 14 Apr 2024 09:13:41 +0000 Subject: [PATCH 10/11] [autofix.ci] apply automated fixes --- .../vue/__tests__/e2e/TransitionGroup.spec.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/vue/__tests__/e2e/TransitionGroup.spec.ts b/packages/vue/__tests__/e2e/TransitionGroup.spec.ts index e18aa915508..da3f4a42de9 100644 --- a/packages/vue/__tests__/e2e/TransitionGroup.spec.ts +++ b/packages/vue/__tests__/e2e/TransitionGroup.spec.ts @@ -534,8 +534,8 @@ describe('e2e: TransitionGroup', () => { show.value ? h('div', { class: 'test' }, 'child') : createCommentVNode('v-if', true) - } - } + }, + }, }, setup: () => { const items = ref([]) @@ -546,7 +546,7 @@ describe('e2e: TransitionGroup', () => { }, duration) } return { click, items } - } + }, }).mount('#app') }, duration) @@ -556,7 +556,7 @@ describe('e2e: TransitionGroup', () => { `
a
` + `
b
` + `
c
` + - `` + ``, ) await transitionFinish(duration) @@ -565,7 +565,7 @@ describe('e2e: TransitionGroup', () => { `
a
` + `
b
` + `
c
` + - `
child
` + `
child
`, ) await transitionFinish(duration) @@ -573,10 +573,10 @@ describe('e2e: TransitionGroup', () => { `
a
` + `
b
` + `
c
` + - `
child
` + `
child
`, ) }, - E2E_TIMEOUT + E2E_TIMEOUT, ) // #4621, #4622, #5153 @@ -601,7 +601,7 @@ describe('e2e: TransitionGroup', () => { show.value = true } return { show, click } - } + }, }) app.config.compilerOptions.whitespace = 'preserve' @@ -613,21 +613,21 @@ describe('e2e: TransitionGroup', () => { expect(await htmlWhenTransitionStart()).toBe( `
foo
` + ` ` + - `
bar
` + `
bar
`, ) await nextFrame() expect(await html('#container')).toBe( `
foo
` + ` ` + - `
bar
` + `
bar
`, ) await transitionFinish(duration) expect(await html('#container')).toBe( - `
foo
` + ` ` + `
bar
` + `
foo
` + ` ` + `
bar
`, ) }, - E2E_TIMEOUT + E2E_TIMEOUT, ) }) From 1f1e9ec72c892aad4a182a195303afaa0c3f8d0f Mon Sep 17 00:00:00 2001 From: Evan You Date: Sun, 14 Apr 2024 23:10:55 +0800 Subject: [PATCH 11/11] refactor: avoid looping over prevChildren twice --- .../src/components/TransitionGroup.ts | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/packages/runtime-dom/src/components/TransitionGroup.ts b/packages/runtime-dom/src/components/TransitionGroup.ts index de9af534f46..763b7a98b24 100644 --- a/packages/runtime-dom/src/components/TransitionGroup.ts +++ b/packages/runtime-dom/src/components/TransitionGroup.ts @@ -112,10 +112,29 @@ const TransitionGroupImpl: ComponentOptions = { tag = 'span' } - // filter out comment nodes and text nodes - prevChildren = - children && - children.filter(child => child.el && child.el instanceof Element) + 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++) { @@ -130,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) } },