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
This commit is contained in:
edison 2024-04-14 23:16:01 +08:00 committed by GitHub
parent c4684d3161
commit 140a7681cc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 145 additions and 12 deletions

View File

@ -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)
}
},

View File

@ -508,4 +508,126 @@ describe('e2e: TransitionGroup', () => {
expect(`<TransitionGroup> 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: `
<div id="container">
<transition-group name="test">
<div v-for="item in items" :key="item" class="test">{{item}}</div>
<Child key="child"/>
</transition-group>
</div>
<button id="toggleBtn" @click="click">button</button>
`,
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(`<!--v-if-->`)
expect(await htmlWhenTransitionStart()).toBe(
`<div class="test test-enter-from test-enter-active">a</div>` +
`<div class="test test-enter-from test-enter-active">b</div>` +
`<div class="test test-enter-from test-enter-active">c</div>` +
`<!--v-if-->`,
)
await transitionFinish(duration)
await nextFrame()
expect(await html('#container')).toBe(
`<div class="test">a</div>` +
`<div class="test">b</div>` +
`<div class="test">c</div>` +
`<div class="test test-enter-active test-enter-to">child</div>`,
)
await transitionFinish(duration)
expect(await html('#container')).toBe(
`<div class="test">a</div>` +
`<div class="test">b</div>` +
`<div class="test">c</div>` +
`<div class="test">child</div>`,
)
},
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: `
<div id="container">
<transition-group name="test">
<div class="test">foo</div>
<div class="test" v-if="show">bar</div>
</transition-group>
</div>
<button id="toggleBtn" @click="click">button</button>
`,
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(`<div class="test">foo</div>` + ` `)
expect(await htmlWhenTransitionStart()).toBe(
`<div class="test">foo</div>` +
` ` +
`<div class="test test-enter-from test-enter-active">bar</div>`,
)
await nextFrame()
expect(await html('#container')).toBe(
`<div class="test">foo</div>` +
` ` +
`<div class="test test-enter-active test-enter-to">bar</div>`,
)
await transitionFinish(duration)
expect(await html('#container')).toBe(
`<div class="test">foo</div>` + ` ` + `<div class="test">bar</div>`,
)
},
E2E_TIMEOUT,
)
})