Skip to content

fix(runtime-core): skip persisted transition hooks when moving kept-alive nodes (fix #14031)#14865

Open
LeSingh1 wants to merge 1 commit into
vuejs:mainfrom
LeSingh1:fix/14031-keep-alive-persisted-transition
Open

fix(runtime-core): skip persisted transition hooks when moving kept-alive nodes (fix #14031)#14865
LeSingh1 wants to merge 1 commit into
vuejs:mainfrom
LeSingh1:fix/14031-keep-alive-persisted-transition

Conversation

@LeSingh1
Copy link
Copy Markdown

@LeSingh1 LeSingh1 commented May 20, 2026

Fixes #14031.

When a <keep-alive> activates or deactivates a cached child, the renderer's move function calls transition.beforeEnter / transition.enter (or leave) on every element vnode that carries a transition. For a normal transition this is correct, but for a persisted transition (the flag the template compiler injects when <Transition> has a single v-show child) the lifecycle is owned by the directive, not by mount/move. The element is being moved in or out of a detached storage container; the visibility is already controlled by v-show.

As a result, every cache hit was firing onBeforeEnter / onEnter and (because no async hook keeps it pending) onAfterEnter, even when the v-show target was never displayed. The reproduction in the issue uses a v-show="false" element inside a <keep-alive> and observes after-enter logged on each toggle.

The fix extends the same !transition.persisted guard that mountElement already uses (via the module-level needTransition helper) to the per-element branch inside move. Persisted transitions still get their hostInsert so the element is correctly relocated between the live tree and the keep-alive storage container, but the directive-owned enter / leave hooks are no longer called behind the directive's back. The existing #13153 interaction with _isLeaving lives outside the persisted path and is unaffected.

Added a regression test in BaseTransition.spec.ts that wraps a KeepAlive switching between two components inside a BaseTransition with persisted: true and asserts that none of the enter or leave hooks fire on activate or deactivate. The test fails on main (the deactivate path calls onBeforeLeave / onLeave and the activate path calls all three enter hooks via the immediate done() in BaseTransition) and passes with the change.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed an issue where transition enter/leave hooks were incorrectly triggered when using persisted transitions within KeepAlive components. Switching between cached components now works smoothly without unintended animation hooks firing.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 833e7846-aeeb-4250-83bf-3f6baae84b60

📥 Commits

Reviewing files that changed from the base of the PR and between 1ce598e and bbca3cd.

📒 Files selected for processing (2)
  • packages/runtime-core/__tests__/components/BaseTransition.spec.ts
  • packages/runtime-core/src/renderer.ts

📝 Walkthrough

Walkthrough

This PR fixes issue #14031 by modifying the renderer's transition decision logic to skip enter/leave hooks for transition.persisted (e.g., v-show-driven) transitions when moving elements. A test validates that persisted transitions inside KeepAlive do not fire hooks during component activation/deactivation cycles.

Changes

Persisted Transition Behavior Fix

Layer / File(s) Summary
Prevent transition hooks for persisted moves
packages/runtime-core/src/renderer.ts, packages/runtime-core/__tests__/components/BaseTransition.spec.ts
The move() function's needTransition condition now checks !transition.persisted to skip transition hook handling for persisted transitions. A new test confirms that switching between cached components inside KeepAlive with a persisted transition does not fire any beforeEnter/enter/afterEnter/beforeLeave/leave/afterLeave hooks during activate/deactivate cycles.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • vuejs/core#13152: Also modifies renderer.ts transition/KeepAlive handling in move() to work around persisted transition and KeepAlive edge cases.
  • vuejs/core#14634: Adjusts transition state synchronization during dynamic slot updates to ensure transition.persisted hook behavior remains consistent.

Suggested labels

scope: transition, :hammer: p3-minor-bug

Suggested reviewers

  • edison1105

Poem

🐰 A transition persists, yet hops not alone—
Trapped in a KeepAlive, the hooks mustn't groan.
Branch-switching whispers, components hide and appear,
But enter and leave stay serene, never dear! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main fix: skipping persisted transition hooks when moving kept-alive nodes, directly addressing issue #14031.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

after-enter event triggered even when transition wrapped in keep-alive never shows its child

1 participant