mirror of https://github.com/vuejs/core.git
fix(transition): ensure Transition enterHooks are updated after clone (#11066)
close #11061
This commit is contained in:
parent
ef2e737577
commit
671cf297a5
|
@ -203,11 +203,13 @@ const BaseTransitionImpl: ComponentOptions = {
|
||||||
return emptyPlaceholder(child)
|
return emptyPlaceholder(child)
|
||||||
}
|
}
|
||||||
|
|
||||||
const enterHooks = resolveTransitionHooks(
|
let enterHooks = resolveTransitionHooks(
|
||||||
innerChild,
|
innerChild,
|
||||||
rawProps,
|
rawProps,
|
||||||
state,
|
state,
|
||||||
instance,
|
instance,
|
||||||
|
// #11061, ensure enterHooks is fresh after clone
|
||||||
|
hooks => (enterHooks = hooks),
|
||||||
)
|
)
|
||||||
setTransitionHooks(innerChild, enterHooks)
|
setTransitionHooks(innerChild, enterHooks)
|
||||||
|
|
||||||
|
@ -305,6 +307,7 @@ export function resolveTransitionHooks(
|
||||||
props: BaseTransitionProps<any>,
|
props: BaseTransitionProps<any>,
|
||||||
state: TransitionState,
|
state: TransitionState,
|
||||||
instance: ComponentInternalInstance,
|
instance: ComponentInternalInstance,
|
||||||
|
postClone?: (hooks: TransitionHooks) => void,
|
||||||
): TransitionHooks {
|
): TransitionHooks {
|
||||||
const {
|
const {
|
||||||
appear,
|
appear,
|
||||||
|
@ -445,7 +448,15 @@ export function resolveTransitionHooks(
|
||||||
},
|
},
|
||||||
|
|
||||||
clone(vnode) {
|
clone(vnode) {
|
||||||
return resolveTransitionHooks(vnode, props, state, instance)
|
const hooks = resolveTransitionHooks(
|
||||||
|
vnode,
|
||||||
|
props,
|
||||||
|
state,
|
||||||
|
instance,
|
||||||
|
postClone,
|
||||||
|
)
|
||||||
|
if (postClone) postClone(hooks)
|
||||||
|
return hooks
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,10 @@ import {
|
||||||
isSuspense,
|
isSuspense,
|
||||||
} from './components/Suspense'
|
} from './components/Suspense'
|
||||||
import type { DirectiveBinding } from './directives'
|
import type { DirectiveBinding } from './directives'
|
||||||
import type { TransitionHooks } from './components/BaseTransition'
|
import {
|
||||||
|
type TransitionHooks,
|
||||||
|
setTransitionHooks,
|
||||||
|
} from './components/BaseTransition'
|
||||||
import { warn } from './warning'
|
import { warn } from './warning'
|
||||||
import {
|
import {
|
||||||
type Teleport,
|
type Teleport,
|
||||||
|
@ -691,7 +694,10 @@ export function cloneVNode<T, U>(
|
||||||
// to clone the transition to ensure that the vnode referenced within
|
// to clone the transition to ensure that the vnode referenced within
|
||||||
// the transition hooks is fresh.
|
// the transition hooks is fresh.
|
||||||
if (transition && cloneTransition) {
|
if (transition && cloneTransition) {
|
||||||
cloned.transition = transition.clone(cloned as VNode)
|
setTransitionHooks(
|
||||||
|
cloned as VNode,
|
||||||
|
transition.clone(cloned as VNode) as TransitionHooks,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (__COMPAT__) {
|
if (__COMPAT__) {
|
||||||
|
|
|
@ -1340,6 +1340,98 @@ describe('e2e: Transition', () => {
|
||||||
E2E_TIMEOUT,
|
E2E_TIMEOUT,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// #11061
|
||||||
|
test(
|
||||||
|
'transition + fallthrough attrs (in-out mode)',
|
||||||
|
async () => {
|
||||||
|
const beforeLeaveSpy = vi.fn()
|
||||||
|
const onLeaveSpy = vi.fn()
|
||||||
|
const afterLeaveSpy = vi.fn()
|
||||||
|
const beforeEnterSpy = vi.fn()
|
||||||
|
const onEnterSpy = vi.fn()
|
||||||
|
const afterEnterSpy = vi.fn()
|
||||||
|
|
||||||
|
await page().exposeFunction('onLeaveSpy', onLeaveSpy)
|
||||||
|
await page().exposeFunction('onEnterSpy', onEnterSpy)
|
||||||
|
await page().exposeFunction('beforeLeaveSpy', beforeLeaveSpy)
|
||||||
|
await page().exposeFunction('beforeEnterSpy', beforeEnterSpy)
|
||||||
|
await page().exposeFunction('afterLeaveSpy', afterLeaveSpy)
|
||||||
|
await page().exposeFunction('afterEnterSpy', afterEnterSpy)
|
||||||
|
|
||||||
|
await page().evaluate(() => {
|
||||||
|
const { onEnterSpy, onLeaveSpy } = window as any
|
||||||
|
const { createApp, ref } = (window as any).Vue
|
||||||
|
createApp({
|
||||||
|
components: {
|
||||||
|
one: {
|
||||||
|
template: '<div>one</div>',
|
||||||
|
},
|
||||||
|
two: {
|
||||||
|
template: '<div>two</div>',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<div id="container">
|
||||||
|
<transition foo="1" name="test" mode="in-out"
|
||||||
|
@before-enter="beforeEnterSpy()"
|
||||||
|
@enter="onEnterSpy()"
|
||||||
|
@after-enter="afterEnterSpy()"
|
||||||
|
@before-leave="beforeLeaveSpy()"
|
||||||
|
@leave="onLeaveSpy()"
|
||||||
|
@after-leave="afterLeaveSpy()">
|
||||||
|
<component :is="view"></component>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
<button id="toggleBtn" @click="click">button</button>
|
||||||
|
`,
|
||||||
|
setup: () => {
|
||||||
|
const view = ref('one')
|
||||||
|
const click = () =>
|
||||||
|
(view.value = view.value === 'one' ? 'two' : 'one')
|
||||||
|
return {
|
||||||
|
view,
|
||||||
|
click,
|
||||||
|
beforeEnterSpy,
|
||||||
|
onEnterSpy,
|
||||||
|
afterEnterSpy,
|
||||||
|
beforeLeaveSpy,
|
||||||
|
onLeaveSpy,
|
||||||
|
afterLeaveSpy,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}).mount('#app')
|
||||||
|
})
|
||||||
|
expect(await html('#container')).toBe('<div foo="1">one</div>')
|
||||||
|
|
||||||
|
// toggle
|
||||||
|
await click('#toggleBtn')
|
||||||
|
await nextTick()
|
||||||
|
await transitionFinish()
|
||||||
|
expect(beforeEnterSpy).toBeCalledTimes(1)
|
||||||
|
expect(onEnterSpy).toBeCalledTimes(1)
|
||||||
|
expect(afterEnterSpy).toBeCalledTimes(1)
|
||||||
|
expect(beforeLeaveSpy).toBeCalledTimes(1)
|
||||||
|
expect(onLeaveSpy).toBeCalledTimes(1)
|
||||||
|
expect(afterLeaveSpy).toBeCalledTimes(1)
|
||||||
|
|
||||||
|
expect(await html('#container')).toBe('<div foo="1" class="">two</div>')
|
||||||
|
|
||||||
|
// toggle back
|
||||||
|
await click('#toggleBtn')
|
||||||
|
await nextTick()
|
||||||
|
await transitionFinish()
|
||||||
|
expect(beforeEnterSpy).toBeCalledTimes(2)
|
||||||
|
expect(onEnterSpy).toBeCalledTimes(2)
|
||||||
|
expect(afterEnterSpy).toBeCalledTimes(2)
|
||||||
|
expect(beforeLeaveSpy).toBeCalledTimes(2)
|
||||||
|
expect(onLeaveSpy).toBeCalledTimes(2)
|
||||||
|
expect(afterLeaveSpy).toBeCalledTimes(2)
|
||||||
|
|
||||||
|
expect(await html('#container')).toBe('<div foo="1" class="">one</div>')
|
||||||
|
},
|
||||||
|
E2E_TIMEOUT,
|
||||||
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'w/ KeepAlive + unmount innerChild',
|
'w/ KeepAlive + unmount innerChild',
|
||||||
async () => {
|
async () => {
|
||||||
|
|
Loading…
Reference in New Issue