Skip to content

fix(KeepAlive): unmount pruned current component when its vnode was cloned on mount - #15711

Merged
edison1105 merged 1 commit into
vuejs:mainfrom
lazerg:fix/issue-15710-keepalive-prune-current
Oct 12, 2026
Merged

edison1105 merged 1 commit into
vuejs:mainfrom
lazerg:fix/issue-15710-keepalive-prune-current

Conversation

@lazerg

@lazerg lazerg commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

close #15710

When KeepAlive re-renders and the child is found in the cache, the returned vnode gets el from the cached one, so normalizeVNode (cloneIfMounted) in renderComponentRoot clones it. That clone is what gets mounted and cached, so current and the cached vnode end up being different objects. When max then prunes the entry of the component on screen, pruneCacheEntry only resets the flag on current. The mounted vnode keeps COMPONENT_SHOULD_KEEP_ALIVE, so it gets deactivated instead of unmounted.

The fix resets the flag on the cached vnode as well. Returning the already-mounted parent vnode, as in the issue, is one way to hit this; changing a prop on the child before switching views does it too.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed a KeepAlive caching issue where a previously mounted component could be unmounted incorrectly when switching components with a cache limit of one.

@coderabbitai

coderabbitai Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Review in Change Stack β†’

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. πŸŽ‰

ℹ️ Recent review info
βš™οΈ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 4c051a2e-dcd8-4403-915d-300d89c38f3a

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 4ab865a and 52d3250.

πŸ“’ Files selected for processing (2)
  • packages/runtime-core/__tests__/components/KeepAlive.spec.ts
  • packages/runtime-core/src/components/KeepAlive.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.


πŸ“ Walkthrough

Walkthrough

When pruneCacheEntry prunes the current entry, it now resets the cached vnode’s KeepAlive flags. A regression test covers reusing a mounted vnode before switching views with max: 1.

Changes

KeepAlive pruning

Layer / File(s) Summary
Reset flags and test current-entry pruning
packages/runtime-core/src/components/KeepAlive.ts, packages/runtime-core/__tests__/components/KeepAlive.spec.ts
pruneCacheEntry resets the cached vnode’s KeepAlive flags when current exists. The regression test checks that the previous component is unmounted once without deactivation.

Priority: βž– Normal

Estimated code review effort: 2 (Simple) | ~8 minutes

Change: Bug fix Β· Severity of issue fixed: Medium

Merge Risk: βšͺ Minimal Β· up to 52d32

The change addresses the reported unmount behavior, with no actionable merge risk identified beyond normal checks.

Security Architecture Review

Security architecture risk: πŸ”΅ Low Β· up to 52d32

The change is confined to component cache teardown and corrects how a reused component is unmounted. No new access path or external dependency is evident, but validation of unusual scheduling cases is limited.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred β€” The demonstrated effect is limited to components affected by KeepAlive current-entry pruning. The evidence does not establish a new cross-service, credential, or data-store exposure.

Trust Boundaries and Controls

  • inferred β€” KeepAlive’s public component entrypoint still reaches the existing internal pruning and renderer paths. The changed flag reset does not itself establish a new trust transition or bypass of a security control.

Resilience and Maintainability Implications

  • inferred β€” Resetting flags before deleting the cache entry preserves the replacement path’s ordering; normal unmount supplies cleanup that deactivation would skip. The supplied regression test covers the reported clone mismatch, not every interrupted or concurrent scheduling case.
πŸš₯ Pre-merge checks | βœ… 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
βœ… Passed checks (4 passed)
Check name Status Explanation
Linked Issues check βœ… Passed Issue #15710 requires a pruned current component to unmount when KeepAlive reuses an already-mounted vnode, including the max: 1 case. pruneCacheEntry now resets the KeepAlive flags on both `current…
Out of Scope Changes check βœ… Passed The reviewed changes modify only KeepAlive.pruneCacheEntry and its regression test. Both changes directly support issue #15710. No unrelated implementation, API, or test changes are present in the s…
Description Check βœ… Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check βœ… Passed The title clearly identifies the KeepAlive fix and the specific condition involving a cloned vnode during mount. It matches the main change and issue objective.
  • Fix all pre-merge checks with AI
✨ Finishing Touches
πŸ§ͺ Generate unit tests (beta)
  • Create a new PR

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.

@pkg-pr-new

pkg-pr-new Bot commented Sep 29, 2026

Copy link
Copy Markdown

Open in StackBlitz

@vue/compiler-core

pnpm add https://pkg.pr.new/@vue/compiler-core@15711
npm i https://pkg.pr.new/@vue/compiler-core@15711
yarn add https://pkg.pr.new/@vue/compiler-core@15711.tgz

@vue/compiler-dom

pnpm add https://pkg.pr.new/@vue/compiler-dom@15711
npm i https://pkg.pr.new/@vue/compiler-dom@15711
yarn add https://pkg.pr.new/@vue/compiler-dom@15711.tgz

@vue/compiler-sfc

pnpm add https://pkg.pr.new/@vue/compiler-sfc@15711
npm i https://pkg.pr.new/@vue/compiler-sfc@15711
yarn add https://pkg.pr.new/@vue/compiler-sfc@15711.tgz

@vue/compiler-ssr

pnpm add https://pkg.pr.new/@vue/compiler-ssr@15711
npm i https://pkg.pr.new/@vue/compiler-ssr@15711
yarn add https://pkg.pr.new/@vue/compiler-ssr@15711.tgz

@vue/reactivity

pnpm add https://pkg.pr.new/@vue/reactivity@15711
npm i https://pkg.pr.new/@vue/reactivity@15711
yarn add https://pkg.pr.new/@vue/reactivity@15711.tgz

@vue/runtime-core

pnpm add https://pkg.pr.new/@vue/runtime-core@15711
npm i https://pkg.pr.new/@vue/runtime-core@15711
yarn add https://pkg.pr.new/@vue/runtime-core@15711.tgz

@vue/runtime-dom

pnpm add https://pkg.pr.new/@vue/runtime-dom@15711
npm i https://pkg.pr.new/@vue/runtime-dom@15711
yarn add https://pkg.pr.new/@vue/runtime-dom@15711.tgz

@vue/server-renderer

pnpm add https://pkg.pr.new/@vue/server-renderer@15711
npm i https://pkg.pr.new/@vue/server-renderer@15711
yarn add https://pkg.pr.new/@vue/server-renderer@15711.tgz

@vue/shared

pnpm add https://pkg.pr.new/@vue/shared@15711
npm i https://pkg.pr.new/@vue/shared@15711
yarn add https://pkg.pr.new/@vue/shared@15711.tgz

vue

pnpm add https://pkg.pr.new/vue@15711
npm i https://pkg.pr.new/vue@15711
yarn add https://pkg.pr.new/vue@15711.tgz

@vue/compat

pnpm add https://pkg.pr.new/@vue/compat@15711
npm i https://pkg.pr.new/@vue/compat@15711
yarn add https://pkg.pr.new/@vue/compat@15711.tgz

commit: 52d3250

@github-actions

Copy link
Copy Markdown

Size Report

Bundles

File Size Gzip Brotli
runtime-dom.global.prod.js 109 kB (+11 B) 41.3 kB (+4 B) 37 kB (+38 B)
vue.global.prod.js 168 kB (+11 B) 61.5 kB (+6 B) 54.6 kB (-10 B)

Usages

Name Size Gzip Brotli
createApp (CAPI only) 50.8 kB 19.6 kB 18 kB
createApp 58.9 kB 22.7 kB 20.8 kB
createSSRApp 63.7 kB 24.7 kB 22.5 kB
defineCustomElement 65.2 kB 24.6 kB 22.5 kB
overall 73.5 kB (+11 B) 28 kB (+6 B) 25.5 kB (+42 B)

@edison1105 edison1105 added ready to merge The PR is ready to be merged. scope: keep-alive labels Sep 29, 2026
@edison1105

Copy link
Copy Markdown
Member

/ecosystem-ci run

@vue-bot

vue-bot commented Oct 12, 2026

Copy link
Copy Markdown
Contributor

πŸ“ Ran ecosystem CI: Open

suite result latest scheduled
router βœ… success βœ… success
primevue βœ… success βœ… success
quasar βœ… success βœ… success
vite-plugin-vue βœ… success βœ… success
pinia βœ… success βœ… success
vuetify βœ… success βœ… success
vue-simple-compiler βœ… success βœ… success
vant βœ… success βœ… success
vitepress βœ… success βœ… success
nuxt βœ… success βœ… success
language-tools ❌ failure ❌ failure
test-utils βœ… success βœ… success
radix-vue βœ… success βœ… success
vueuse βœ… success βœ… success
vue-macros βœ… success βœ… success
vue-i18n βœ… success βœ… success

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

Labels

ready to merge The PR is ready to be merged. scope: keep-alive

Projects

None yet

Development

Successfully merging this pull request may close these issues.

KeepAlive with max deactivates instead of unmounting the pruned current component when the render returned an already-mounted vnode (instance leaks)

3 participants