mirror of https://github.com/vuejs/core.git
fix(Transition): handle KeepAlive + transition leaving edge case (#13152)
close #13153
This commit is contained in:
parent
7f60ef83e7
commit
3190b179b0
|
@ -24,7 +24,7 @@ import { SchedulerJobFlags } from '../scheduler'
|
||||||
|
|
||||||
type Hook<T = () => void> = T | T[]
|
type Hook<T = () => void> = T | T[]
|
||||||
|
|
||||||
const leaveCbKey: unique symbol = Symbol('_leaveCb')
|
export const leaveCbKey: unique symbol = Symbol('_leaveCb')
|
||||||
const enterCbKey: unique symbol = Symbol('_enterCb')
|
const enterCbKey: unique symbol = Symbol('_enterCb')
|
||||||
|
|
||||||
export interface BaseTransitionProps<HostElement = RendererElement> {
|
export interface BaseTransitionProps<HostElement = RendererElement> {
|
||||||
|
|
|
@ -85,7 +85,7 @@ import { initFeatureFlags } from './featureFlags'
|
||||||
import { isAsyncWrapper } from './apiAsyncComponent'
|
import { isAsyncWrapper } from './apiAsyncComponent'
|
||||||
import { isCompatEnabled } from './compat/compatConfig'
|
import { isCompatEnabled } from './compat/compatConfig'
|
||||||
import { DeprecationTypes } from './compat/compatConfig'
|
import { DeprecationTypes } from './compat/compatConfig'
|
||||||
import type { TransitionHooks } from './components/BaseTransition'
|
import { type TransitionHooks, leaveCbKey } from './components/BaseTransition'
|
||||||
import type { VueElement } from '@vue/runtime-dom'
|
import type { VueElement } from '@vue/runtime-dom'
|
||||||
|
|
||||||
export interface Renderer<HostElement = RendererElement> {
|
export interface Renderer<HostElement = RendererElement> {
|
||||||
|
@ -2070,6 +2070,12 @@ function baseCreateRenderer(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const performLeave = () => {
|
const performLeave = () => {
|
||||||
|
// #13153 move kept-alive node before v-show transition leave finishes
|
||||||
|
// it needs to call the leaving callback to ensure element's `display`
|
||||||
|
// is `none`
|
||||||
|
if (el!._isLeaving) {
|
||||||
|
el
|
||||||
|
}
|
||||||
leave(el!, () => {
|
leave(el!, () => {
|
||||||
remove()
|
remove()
|
||||||
afterLeave && afterLeave()
|
afterLeave && afterLeave()
|
||||||
|
|
|
@ -1722,6 +1722,107 @@ describe('e2e: Transition', () => {
|
||||||
},
|
},
|
||||||
E2E_TIMEOUT,
|
E2E_TIMEOUT,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// #13153
|
||||||
|
test(
|
||||||
|
'move kept-alive node before v-show transition leave finishes',
|
||||||
|
async () => {
|
||||||
|
await page().evaluate(() => {
|
||||||
|
const { createApp, ref } = (window as any).Vue
|
||||||
|
const show = ref(true)
|
||||||
|
createApp({
|
||||||
|
template: `
|
||||||
|
<div id="container">
|
||||||
|
<KeepAlive :include="['Comp1', 'Comp2']">
|
||||||
|
<component :is="state === 1 ? 'Comp1' : 'Comp2'"/>
|
||||||
|
</KeepAlive>
|
||||||
|
</div>
|
||||||
|
<button id="toggleBtn" @click="click">button</button>
|
||||||
|
`,
|
||||||
|
setup: () => {
|
||||||
|
const state = ref(1)
|
||||||
|
const click = () => (state.value = state.value === 1 ? 2 : 1)
|
||||||
|
return { state, click }
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Comp1: {
|
||||||
|
components: {
|
||||||
|
Item: {
|
||||||
|
name: 'Item',
|
||||||
|
setup() {
|
||||||
|
return { show }
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<Transition name="test">
|
||||||
|
<div v-show="show" >
|
||||||
|
<h2>{{ show ? "I should show" : "I shouldn't show " }}</h2>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
name: 'Comp1',
|
||||||
|
setup() {
|
||||||
|
const toggle = () => (show.value = !show.value)
|
||||||
|
return { show, toggle }
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<Item />
|
||||||
|
<h2>This is page1</h2>
|
||||||
|
<button id="changeShowBtn" @click="toggle">{{ show }}</button>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
Comp2: {
|
||||||
|
name: 'Comp2',
|
||||||
|
template: `<h2>This is page2</h2>`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).mount('#app')
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(await html('#container')).toBe(
|
||||||
|
`<div><h2>I should show</h2></div>` +
|
||||||
|
`<h2>This is page1</h2>` +
|
||||||
|
`<button id="changeShowBtn">true</button>`,
|
||||||
|
)
|
||||||
|
|
||||||
|
// trigger v-show transition leave
|
||||||
|
await click('#changeShowBtn')
|
||||||
|
await nextTick()
|
||||||
|
expect(await html('#container')).toBe(
|
||||||
|
`<div class="test-leave-from test-leave-active"><h2>I shouldn't show </h2></div>` +
|
||||||
|
`<h2>This is page1</h2>` +
|
||||||
|
`<button id="changeShowBtn">false</button>`,
|
||||||
|
)
|
||||||
|
|
||||||
|
// switch to page2, before leave finishes
|
||||||
|
// expect v-show element's display to be none
|
||||||
|
await click('#toggleBtn')
|
||||||
|
await nextTick()
|
||||||
|
expect(await html('#container')).toBe(
|
||||||
|
`<div class="test-leave-from test-leave-active" style="display: none;"><h2>I shouldn't show </h2></div>` +
|
||||||
|
`<h2>This is page2</h2>`,
|
||||||
|
)
|
||||||
|
|
||||||
|
// switch back to page1
|
||||||
|
// expect v-show element's display to be none
|
||||||
|
await click('#toggleBtn')
|
||||||
|
await nextTick()
|
||||||
|
expect(await html('#container')).toBe(
|
||||||
|
`<div class="test-enter-from test-enter-active" style="display: none;"><h2>I shouldn't show </h2></div>` +
|
||||||
|
`<h2>This is page1</h2>` +
|
||||||
|
`<button id="changeShowBtn">false</button>`,
|
||||||
|
)
|
||||||
|
|
||||||
|
await transitionFinish()
|
||||||
|
expect(await html('#container')).toBe(
|
||||||
|
`<div class="" style="display: none;"><h2>I shouldn't show </h2></div>` +
|
||||||
|
`<h2>This is page1</h2>` +
|
||||||
|
`<button id="changeShowBtn">false</button>`,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
E2E_TIMEOUT,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('transition with Suspense', () => {
|
describe('transition with Suspense', () => {
|
||||||
|
|
Loading…
Reference in New Issue