diff --git a/packages/runtime-core/src/componentRenderUtils.ts b/packages/runtime-core/src/componentRenderUtils.ts index 5badb04b006..a15d18d56bf 100644 --- a/packages/runtime-core/src/componentRenderUtils.ts +++ b/packages/runtime-core/src/componentRenderUtils.ts @@ -27,6 +27,7 @@ import { warnDeprecation, } from './compat/compatConfig' import { shallowReadonly } from '@vue/reactivity' +import { setTransitionHooks } from './components/BaseTransition' /** * dev only flag to track whether $attrs was used during render. @@ -253,7 +254,7 @@ export function renderComponentRoot( `that cannot be animated.`, ) } - root.transition = vnode.transition + setTransitionHooks(root, vnode.transition) } if (__DEV__ && setRoot) { diff --git a/packages/runtime-core/src/components/BaseTransition.ts b/packages/runtime-core/src/components/BaseTransition.ts index 37534ad699f..6ce06d28239 100644 --- a/packages/runtime-core/src/components/BaseTransition.ts +++ b/packages/runtime-core/src/components/BaseTransition.ts @@ -227,6 +227,7 @@ const BaseTransitionImpl: ComponentOptions = { if (!(instance.job.flags! & SchedulerJobFlags.DISPOSED)) { instance.update() } + delete leavingHooks.afterLeave } return emptyPlaceholder(child) } else if (mode === 'in-out' && innerChild.type !== Comment) { @@ -515,6 +516,7 @@ function getInnerChild(vnode: VNode): VNode | undefined { export function setTransitionHooks(vnode: VNode, hooks: TransitionHooks): void { if (vnode.shapeFlag & ShapeFlags.COMPONENT && vnode.component) { + vnode.transition = hooks setTransitionHooks(vnode.component.subTree, hooks) } else if (__FEATURE_SUSPENSE__ && vnode.shapeFlag & ShapeFlags.SUSPENSE) { vnode.ssContent!.transition = hooks.clone(vnode.ssContent!) diff --git a/packages/runtime-core/src/components/KeepAlive.ts b/packages/runtime-core/src/components/KeepAlive.ts index a87f44cc8fa..dd1d1f5a6e3 100644 --- a/packages/runtime-core/src/components/KeepAlive.ts +++ b/packages/runtime-core/src/components/KeepAlive.ts @@ -267,7 +267,7 @@ const KeepAliveImpl: ComponentOptions = { pendingCacheKey = null if (!slots.default) { - return null + return (current = null) } const children = slots.default() diff --git a/packages/vue/__tests__/e2e/Transition.spec.ts b/packages/vue/__tests__/e2e/Transition.spec.ts index 9a5375e72a2..c0863a75991 100644 --- a/packages/vue/__tests__/e2e/Transition.spec.ts +++ b/packages/vue/__tests__/e2e/Transition.spec.ts @@ -1427,9 +1427,11 @@ describe('e2e: Transition', () => { }, E2E_TIMEOUT, ) + }) + describe('transition with KeepAlive', () => { test( - 'w/ KeepAlive + unmount innerChild', + 'unmount innerChild (out-in mode)', async () => { const unmountSpy = vi.fn() await page().exposeFunction('unmountSpy', unmountSpy) @@ -1484,6 +1486,173 @@ describe('e2e: Transition', () => { }, E2E_TIMEOUT, ) + + // #11775 + test( + 'switch child then update include (out-in mode)', + async () => { + const onUpdatedSpyA = vi.fn() + const onUnmountedSpyC = vi.fn() + + await page().exposeFunction('onUpdatedSpyA', onUpdatedSpyA) + await page().exposeFunction('onUnmountedSpyC', onUnmountedSpyC) + + await page().evaluate(() => { + const { onUpdatedSpyA, onUnmountedSpyC } = window as any + const { createApp, ref, shallowRef, h, onUpdated, onUnmounted } = ( + window as any + ).Vue + createApp({ + template: ` +