mirror of https://github.com/vuejs/core.git
fix(runtime-core): prevent unmounted vnode from being inserted during transition leave (#12862)
close #12860
This commit is contained in:
parent
263f63f735
commit
d6a6ec13ce
|
@ -187,6 +187,11 @@ const KeepAliveImpl: ComponentOptions = {
|
|||
// Update components tree
|
||||
devtoolsComponentAdded(instance)
|
||||
}
|
||||
|
||||
// for e2e test
|
||||
if (__DEV__ && __BROWSER__) {
|
||||
;(instance as any).__keepAliveStorageContainer = storageContainer
|
||||
}
|
||||
}
|
||||
|
||||
function unmount(vnode: VNode) {
|
||||
|
|
|
@ -2049,7 +2049,13 @@ function baseCreateRenderer(
|
|||
queuePostRenderEffect(() => transition!.enter(el!), parentSuspense)
|
||||
} else {
|
||||
const { leave, delayLeave, afterLeave } = transition!
|
||||
const remove = () => hostInsert(el!, container, anchor)
|
||||
const remove = () => {
|
||||
if (vnode.ctx!.isUnmounted) {
|
||||
hostRemove(el!)
|
||||
} else {
|
||||
hostInsert(el!, container, anchor)
|
||||
}
|
||||
}
|
||||
const performLeave = () => {
|
||||
leave(el!, () => {
|
||||
remove()
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import type { ElementHandle } from 'puppeteer'
|
||||
import { E2E_TIMEOUT, setupPuppeteer } from './e2eUtils'
|
||||
import path from 'node:path'
|
||||
import { Transition, createApp, h, nextTick, ref } from 'vue'
|
||||
|
@ -1653,6 +1654,74 @@ describe('e2e: Transition', () => {
|
|||
},
|
||||
E2E_TIMEOUT,
|
||||
)
|
||||
|
||||
// #12860
|
||||
test(
|
||||
'unmount children',
|
||||
async () => {
|
||||
const unmountSpy = vi.fn()
|
||||
let storageContainer: ElementHandle<HTMLDivElement>
|
||||
const setStorageContainer = (container: any) =>
|
||||
(storageContainer = container)
|
||||
await page().exposeFunction('unmountSpy', unmountSpy)
|
||||
await page().exposeFunction('setStorageContainer', setStorageContainer)
|
||||
await page().evaluate(() => {
|
||||
const { unmountSpy, setStorageContainer } = window as any
|
||||
const { createApp, ref, h, onUnmounted, getCurrentInstance } = (
|
||||
window as any
|
||||
).Vue
|
||||
createApp({
|
||||
template: `
|
||||
<div id="container">
|
||||
<transition>
|
||||
<KeepAlive :include="includeRef">
|
||||
<TrueBranch v-if="toggle"></TrueBranch>
|
||||
</KeepAlive>
|
||||
</transition>
|
||||
</div>
|
||||
<button id="toggleBtn" @click="click">button</button>
|
||||
`,
|
||||
components: {
|
||||
TrueBranch: {
|
||||
name: 'TrueBranch',
|
||||
setup() {
|
||||
const instance = getCurrentInstance()
|
||||
onUnmounted(() => {
|
||||
unmountSpy()
|
||||
setStorageContainer(instance.__keepAliveStorageContainer)
|
||||
})
|
||||
const count = ref(0)
|
||||
return () => h('div', count.value)
|
||||
},
|
||||
},
|
||||
},
|
||||
setup: () => {
|
||||
const includeRef = ref(['TrueBranch'])
|
||||
const toggle = ref(true)
|
||||
const click = () => {
|
||||
toggle.value = !toggle.value
|
||||
if (toggle.value) {
|
||||
includeRef.value = ['TrueBranch']
|
||||
} else {
|
||||
includeRef.value = []
|
||||
}
|
||||
}
|
||||
return { toggle, click, unmountSpy, includeRef }
|
||||
},
|
||||
}).mount('#app')
|
||||
})
|
||||
|
||||
await transitionFinish()
|
||||
expect(await html('#container')).toBe('<div>0</div>')
|
||||
|
||||
await click('#toggleBtn')
|
||||
await transitionFinish()
|
||||
expect(await html('#container')).toBe('<!--v-if-->')
|
||||
expect(unmountSpy).toBeCalledTimes(1)
|
||||
expect(await storageContainer!.evaluate(x => x.innerHTML)).toBe(``)
|
||||
},
|
||||
E2E_TIMEOUT,
|
||||
)
|
||||
})
|
||||
|
||||
describe('transition with Suspense', () => {
|
||||
|
|
Loading…
Reference in New Issue