Skip to content

fix(suspense): update suspense vnode's el during branch self-update#12922

Merged
edison1105 merged 9 commits intomainfrom
edison/fix/suspenseChildrenSelfUpdate
Mar 25, 2026
Merged

fix(suspense): update suspense vnode's el during branch self-update#12922
edison1105 merged 9 commits intomainfrom
edison/fix/suspenseChildrenSelfUpdate

Conversation

@edison1105
Copy link
Copy Markdown
Member

@edison1105 edison1105 commented Feb 20, 2025

close #12920

Summary by CodeRabbit

  • Bug Fixes

    • Preserve host element identity through wrapper components around Suspense boundaries after async child updates.
    • Ensure Suspense boundaries unmount cleanly after async child resolution, leaving correct DOM markers.
  • Tests

    • Added regression tests for Suspense with async components (factory and async-setup), covering unmounting and host-element identity cases.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Feb 20, 2025

Size Report

Bundles

File Size Gzip Brotli
runtime-dom.global.prod.js 105 kB (+69 B) 39.8 kB (+17 B) 35.8 kB (+12 B)
vue.global.prod.js 164 kB (+69 B) 59.8 kB (+15 B) 53.2 kB (+7 B)

Usages

Name Size Gzip Brotli
createApp (CAPI only) 48.3 kB (+69 B) 18.8 kB (+17 B) 17.2 kB (+15 B)
createApp 56.5 kB (+69 B) 21.8 kB (+18 B) 19.9 kB (+5 B)
createSSRApp 60.7 kB (+69 B) 23.6 kB (+23 B) 21.5 kB (+13 B)
defineCustomElement 62.6 kB (+69 B) 23.8 kB (+14 B) 21.7 kB (+17 B)
overall 71 kB (+69 B) 27.2 kB (+20 B) 24.8 kB (-6 B)

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Feb 20, 2025

Open in StackBlitz

@vue/compiler-core

pnpm add https://pkg.pr.new/@vue/compiler-core@12922
npm i https://pkg.pr.new/@vue/compiler-core@12922
yarn add https://pkg.pr.new/@vue/[email protected]

@vue/compiler-dom

pnpm add https://pkg.pr.new/@vue/compiler-dom@12922
npm i https://pkg.pr.new/@vue/compiler-dom@12922
yarn add https://pkg.pr.new/@vue/[email protected]

@vue/compiler-sfc

pnpm add https://pkg.pr.new/@vue/compiler-sfc@12922
npm i https://pkg.pr.new/@vue/compiler-sfc@12922
yarn add https://pkg.pr.new/@vue/[email protected]

@vue/compiler-ssr

pnpm add https://pkg.pr.new/@vue/compiler-ssr@12922
npm i https://pkg.pr.new/@vue/compiler-ssr@12922
yarn add https://pkg.pr.new/@vue/[email protected]

@vue/reactivity

pnpm add https://pkg.pr.new/@vue/reactivity@12922
npm i https://pkg.pr.new/@vue/reactivity@12922
yarn add https://pkg.pr.new/@vue/[email protected]

@vue/runtime-core

pnpm add https://pkg.pr.new/@vue/runtime-core@12922
npm i https://pkg.pr.new/@vue/runtime-core@12922
yarn add https://pkg.pr.new/@vue/[email protected]

@vue/runtime-dom

pnpm add https://pkg.pr.new/@vue/runtime-dom@12922
npm i https://pkg.pr.new/@vue/runtime-dom@12922
yarn add https://pkg.pr.new/@vue/[email protected]

@vue/server-renderer

pnpm add https://pkg.pr.new/@vue/server-renderer@12922
npm i https://pkg.pr.new/@vue/server-renderer@12922
yarn add https://pkg.pr.new/@vue/[email protected]

@vue/shared

pnpm add https://pkg.pr.new/@vue/shared@12922
npm i https://pkg.pr.new/@vue/shared@12922
yarn add https://pkg.pr.new/@vue/[email protected]

vue

pnpm add https://pkg.pr.new/vue@12922
npm i https://pkg.pr.new/vue@12922
yarn add https://pkg.pr.new/[email protected]

@vue/compat

pnpm add https://pkg.pr.new/@vue/compat@12922
npm i https://pkg.pr.new/@vue/compat@12922
yarn add https://pkg.pr.new/@vue/[email protected]

commit: d08ec47

@edison1105 edison1105 force-pushed the edison/fix/suspenseChildrenSelfUpdate branch 2 times, most recently from 61ebab0 to 1b01b0f Compare February 21, 2025 01:15
@edison1105 edison1105 added ready to merge The PR is ready to be merged. scope: suspense 🔨 p3-minor-bug Priority 3: this fixes a bug, but is an edge case that only affects very specific usage. labels Feb 21, 2025
@edison1105 edison1105 changed the title fix(runtime-core): synchronously update Suspense vnode's el during component self-update fix(Suspense): synchronously update Suspense vnode's el during children self-update Feb 21, 2025
@edison1105 edison1105 force-pushed the edison/fix/suspenseChildrenSelfUpdate branch from 1b01b0f to c52332a Compare February 21, 2025 06:33
@edison1105 edison1105 marked this pull request as draft February 21, 2025 08:54
@edison1105 edison1105 removed the ready to merge The PR is ready to be merged. label Feb 21, 2025
@edison1105 edison1105 changed the title fix(Suspense): synchronously update Suspense vnode's el during children self-update fix(Suspense): also update Suspense vnode's el during branch self-update Feb 21, 2025
@edison1105 edison1105 marked this pull request as ready for review February 21, 2025 12:40
@edison1105 edison1105 changed the title fix(Suspense): also update Suspense vnode's el during branch self-update fix(Suspense): update Suspense vnode's el during branch self-update Feb 24, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 25, 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: b944c4b2-8e4b-4621-8f5b-c7ef82d76247

📥 Commits

Reviewing files that changed from the base of the PR and between 62b8d29 and c23cf87.

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

📝 Walkthrough

Walkthrough

Propagates host element identity through HOC chains into Suspense vnodes by updating updateHOCHostEl to consider a parent’s suspense.activeBranch; adds regression tests exercising Suspense unmount and host-el identity after async child self-updates.

Changes

Cohort / File(s) Summary
Suspense tests
packages/runtime-core/__tests__/components/Suspense.spec.ts
Added three regression tests for issue #12920: unmounting a <Suspense> after an async child self-updates (two variants) and verifying host element identity propagation through wrapper components; adjusted test harness imports (block-level helpers) and renamed defineAsyncComponent import.
Host-el propagation
packages/runtime-core/src/componentRenderUtils.ts
Changed updateHOCHostEl signature to accept suspense; during parent walking, when encountering a parent whose root.suspense.activeBranch === vnode, also set root.suspense.vnode.el = root.el = el, reassign vnode = root to continue propagation, and perform a final alignment updating suspense.vnode.el if needed.

Sequence Diagram(s)

sequenceDiagram
    participant AsyncComp as AsyncComponent
    participant Suspense as SuspenseBoundary
    participant HOC as HOC/ParentChain
    participant Host as HostDOM

    AsyncComp->>Suspense: resolve & mount (becomes activeBranch)
    Suspense->>HOC: activeBranch referenced in parent chain
    HOC->>Host: host el assigned (updateHOCHostEl invoked)
    note right of HOC: walker detects root.suspense.activeBranch === vnode
    HOC->>Suspense: propagate el to suspension vnode (suspense.vnode.el = el)
    Suspense->>Host: suspense vnode aligned with host el
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hopped through wrappers, nose to the el,
Chased async shadows where fallbacks dwell,
I nudged Suspense’s branch till roots were aligned,
A tiny hop fixed the DOM in kind,
Crunch of a carrot — all components well.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: fixing suspense vnode element updates during async child self-updates, which directly addresses the issue of Suspense unmounting with errors.
Linked Issues check ✅ Passed The changes directly address issue #12920 by fixing the suspense vnode's element propagation during branch self-updates, implementing the required fix to prevent crashes on Suspense unmount.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the suspense element propagation issue: test additions verify the fix, and the componentRenderUtils modification implements the required solution without unrelated alterations.

✏️ 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/suspenseChildrenSelfUpdate

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

@edison1105 edison1105 changed the title fix(Suspense): update Suspense vnode's el during branch self-update fix(suspense): update Suspense vnode's el during branch self-update Mar 25, 2026
@edison1105 edison1105 changed the title fix(suspense): update Suspense vnode's el during branch self-update fix(suspense): update suspense vnode's el during branch self-update Mar 25, 2026
@edison1105 edison1105 added the ready to merge The PR is ready to be merged. label Mar 25, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/runtime-core/__tests__/components/Suspense.spec.ts`:
- Around line 2246-2255: The test's async setup() helpers create a Promise p and
push its resolution into deps, but setup() does not await p so the component's
Suspense dependency resolves immediately; modify the async setup functions (the
async setup() helpers that define const p = new Promise(...) and push to deps)
to await p before returning so the component truly remains pending, and keep
pushing the same p (or its chained Promise) into deps to preserve the extra tick
for Node12; apply the same change to both occurrences referenced around the
async setup blocks at the two spots noted.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0fa9e763-4900-4e3c-8f27-e5edc26286cf

📥 Commits

Reviewing files that changed from the base of the PR and between d18c248 and 2ac4b62.

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

@vuejs vuejs deleted a comment from edison1105 Mar 25, 2026
@vuejs vuejs deleted a comment from edison1105 Mar 25, 2026
@vuejs vuejs deleted a comment from edison1105 Mar 25, 2026
@edison1105
Copy link
Copy Markdown
Member Author

/ecosystem-ci run

@vue-bot
Copy link
Copy Markdown
Contributor

vue-bot commented Mar 25, 2026

📝 Ran ecosystem CI: Open

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

@vuejs vuejs deleted a comment from edison1105 Mar 25, 2026
@edison1105 edison1105 merged commit a2c1700 into main Mar 25, 2026
15 of 16 checks passed
@edison1105 edison1105 deleted the edison/fix/suspenseChildrenSelfUpdate branch March 25, 2026 08:28
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. ready to merge The PR is ready to be merged. scope: suspense

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Suspense unmounts with an error if it renders a component with a root node implementing a client-only behavior

2 participants