Skip to content

fix(teleport): handle pending updates before deferred mount#14642

Open
edison1105 wants to merge 3 commits intomainfrom
edison/fix/14640
Open

fix(teleport): handle pending updates before deferred mount#14642
edison1105 wants to merge 3 commits intomainfrom
edison/fix/14640

Conversation

@edison1105
Copy link
Copy Markdown
Member

@edison1105 edison1105 commented Mar 27, 2026

close #14640

Summary by CodeRabbit

  • Bug Fixes
    • Teleport deferring and mounting are more robust: deferred teleports no longer mount or remove incorrectly when suspense or pending state is involved, and deferred updates correctly apply the latest rendered content.
  • Tests
    • Added unit and E2E tests covering Suspense+Teleport interactions, deferred teleport updates, and transition scenarios to prevent regressions.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 27, 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: 96eb1b35-227e-4d0d-a2e3-12c49a2696d7

📥 Commits

Reviewing files that changed from the base of the PR and between 2492738 and e9baf39.

📒 Files selected for processing (4)
  • packages/runtime-core/__tests__/components/Suspense.spec.ts
  • packages/runtime-core/__tests__/components/Teleport.spec.ts
  • packages/runtime-core/src/components/Teleport.ts
  • packages/vue/__tests__/e2e/Transition.spec.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/runtime-core/src/components/Teleport.ts

📝 Walkthrough

Walkthrough

Teleport now uses scheduler-backed tracking for deferred/suspense-pending mounts and updates via a WeakMap of pending mount/update jobs keyed by vnode.el. Mount/update jobs are queued post-render and disposed or replaced when superseded; removal checks pending state to avoid unmounting teleported children that never mounted.

Changes

Cohort / File(s) Summary
Teleport runtime
packages/runtime-core/src/components/Teleport.ts
Replaces vnode.el!.__isMounted flag with a WeakMap<el, PendingTeleportState> and scheduler-posted mountJob/updateJob. Mounts/updates are enqueued for deferred teleports or when inside a Suspense pending branch; queued jobs are tracked, replaceable, and disposed when superseded. remove now derives isMounted from pending state and avoids removing children that never mounted.
Suspense tests
packages/runtime-core/__tests__/components/Suspense.spec.ts
Adds two tests covering Teleport inside Suspense: ensure teleported nodes are discarded when the Teleport subtree becomes inactive before async resolve, including toggling disabled while pending.
Deferred Teleport tests
packages/runtime-core/__tests__/components/Teleport.spec.ts
Adds a deferred-teleport test ensuring the mounted vnode remains the patch base across deferred updates and final teleported content reflects latest state after queued updates.
E2E transition+Suspense
packages/vue/__tests__/e2e/Transition.spec.ts
Adds an E2E test for Transition with Suspense + Teleport switching keyed branches mid-transition to validate teleport markers and branch updates across transitions and suspense resolution.

Sequence Diagram(s)

sequenceDiagram
  participant App as "App / Component"
  participant Suspense as "Suspense"
  participant Teleport as "TeleportImpl"
  participant Scheduler as "Scheduler (postFlush)"
  participant Target as "Teleport Target (DOM)"

  rect rgba(0,128,0,0.5)
  App->>Suspense: render async branch (may include Teleport)
  Suspense->>Teleport: patch vnode (mount/update)
  end

  rect rgba(0,0,255,0.5)
  Teleport->>Teleport: check isTeleportDeferred(props) OR parentSuspense.pendingBranch
  alt defer needed
    Teleport->>Scheduler: enqueue mountJob / updateJob (track in WeakMap)
  else immediate
    Teleport->>Target: mount/patch children
  end
  end

  rect rgba(128,0,128,0.5)
  Scheduler->>Teleport: run queued job (post-flush)
  Teleport->>Teleport: verify pending state matches job
  alt still valid
    Teleport->>Target: mount/patch children
  else disposed/superseded
    Teleport->>Scheduler: dispose job (no DOM changes)
  end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

ready to merge, scope: teleport, scope: suspense, :hammer: p3-minor-bug

Poem

🐰 I waited in a Suspensey glen, teleport queued on my paw,
Jobs lined up in a WeakMap, tracked without a flaw.
If a branch hops away before the mount can land,
I cancel the old job and tidy the sand.
Hooray — no stray leaves, the DOM stays grand! 🌿

🚥 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 PR title 'fix(teleport): handle pending updates before deferred mount' directly describes the core change addressing deferred Teleport mount handling to fix the Suspense branch swap issue.
Linked Issues check ✅ Passed The PR fixes all key objectives: prevents scheduler requeue loops during deferred Teleport callbacks, safely handles discarded Suspense mounts, and restores correct unmount behavior matching pre-regression 3.5.30 behavior.
Out of Scope Changes check ✅ Passed All changes focus on Teleport deferred-mount handling and Suspense interactions; test additions cover the specific regression scenarios without introducing unrelated modifications.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch edison/fix/14640

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

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Mar 27, 2026

Open in StackBlitz

@vue/compiler-core

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

@vue/compiler-dom

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

@vue/compiler-sfc

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

@vue/compiler-ssr

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

@vue/reactivity

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

@vue/runtime-core

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

@vue/runtime-dom

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

@vue/server-renderer

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

@vue/shared

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

vue

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

@vue/compat

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

commit: e9baf39

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 27, 2026

Size Report

Bundles

File Size Gzip Brotli
runtime-dom.global.prod.js 106 kB (+657 B) 40 kB (+227 B) 36 kB (+198 B)
vue.global.prod.js 164 kB (+657 B) 60 kB (+227 B) 53.4 kB (+181 B)

Usages

Name Size Gzip Brotli
createApp (CAPI only) 48.3 kB 18.8 kB 17.2 kB
createApp 56.5 kB 21.8 kB 19.9 kB
createSSRApp 60.7 kB 23.6 kB 21.5 kB
defineCustomElement 62.6 kB 23.8 kB 21.7 kB
overall 71 kB 27.2 kB 24.7 kB

@edison1105 edison1105 changed the title fix(teleport): avoid requeue loop during suspense branch swap fix(teleport): handle pending updates before deferred mount Mar 27, 2026
@edison1105 edison1105 added scope: teleport 🔨 p3-minor-bug Priority 3: this fixes a bug, but is an edge case that only affects very specific usage. labels Mar 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔨 p3-minor-bug Priority 3: this fixes a bug, but is an edge case that only affects very specific usage. scope: teleport

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Teleport + Suspense unmount crash during page navigation in Nuxt SSR (regression in 3.5.31)

1 participant