Skip to content

fix(hydration): avoid component double mount during hydration#14690

Merged
edison1105 merged 2 commits intominorfrom
edison/fix/hydrationBugs
Apr 7, 2026
Merged

fix(hydration): avoid component double mount during hydration#14690
edison1105 merged 2 commits intominorfrom
edison/fix/hydrationBugs

Conversation

@edison1105
Copy link
Copy Markdown
Member

@edison1105 edison1105 commented Apr 7, 2026

Summary by CodeRabbit

  • Bug Fixes

    • Fixed an issue where component lifecycle hooks were executed multiple times during server-side rendering hydration.
    • Ensured lifecycle hooks like beforeMount execute exactly once during component hydration and interop operations.
  • Tests

    • Added comprehensive regression tests for hydration behavior.
    • Covered scenarios including standard components, async setup, and Suspense-wrapped components.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 7, 2026

🚥 Pre-merge checks | ✅ 2 | ❌ 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%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(hydration): avoid component double mount during hydration' accurately describes the main change - preventing duplicate component mounting during hydration by introducing a managedMount parameter.

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

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

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 7, 2026

Size Report

Bundles

File Size Gzip Brotli
compiler-dom.global.prod.js 86.4 kB 30.3 kB 26.6 kB
runtime-dom.global.prod.js 112 kB 42.2 kB 37.8 kB
vue.global.prod.js 171 kB 62 kB 55.3 kB

Usages

Name Size Gzip Brotli
createApp (CAPI only) 51.1 kB 19.9 kB 18.2 kB
createApp 60.2 kB 23.3 kB 21.2 kB
createApp + vaporInteropPlugin 88.4 kB (+12 B) 32.5 kB (+3 B) 29.4 kB (+27 B)
createVaporApp 28.9 kB (+9 B) 11.2 kB (+1 B) 10.3 kB (+8 B)
createSSRApp 64.7 kB 25 kB 22.7 kB
createVaporSSRApp 32.4 kB (+12 B) 12.5 kB (+8 B) 11.5 kB (+9 B)
defineCustomElement 66.7 kB 25.2 kB 22.9 kB
defineVaporCustomElement 39.8 kB (+12 B) 14.7 kB (+9 B) 13.5 kB (+17 B)
overall 75.2 kB 28.6 kB 25.9 kB

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 7, 2026

Open in StackBlitz

@vue/compiler-core

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

@vue/compiler-dom

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

@vue/compiler-sfc

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

@vue/compiler-ssr

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

@vue/compiler-vapor

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

@vue/reactivity

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

@vue/runtime-core

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

@vue/runtime-dom

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

@vue/runtime-vapor

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

@vue/server-renderer

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

@vue/shared

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

vue

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

@vue/compat

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

commit: ffd5d16

@edison1105 edison1105 added the scope: vapor related to vapor mode label Apr 7, 2026
@edison1105 edison1105 changed the title fix(runtime-vapor): avoid double mount in interop hydration fix(runtime-vapor): avoid double mount in vdom interop Apr 7, 2026
@edison1105 edison1105 changed the title fix(runtime-vapor): avoid double mount in vdom interop fix(hydration): avoid double mount in vdom interop Apr 7, 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.

🧹 Nitpick comments (1)
packages/runtime-vapor/__tests__/hydration.spec.ts (1)

4688-4747: Avoid fixed setTimeout(10) synchronization in this hydration test.

This test can become timing-sensitive on slower CI runners. Prefer a deterministic async gate so the assertion does not depend on wall-clock timing.

Proposed deterministic test sync
-          const beforeMount = vi.fn()
-          const data = ref({ beforeMount })
+          const beforeMount = vi.fn()
+          let resolveAsyncSetup!: () => void
+          const serverData = ref({
+            beforeMount,
+            ready: Promise.resolve(),
+          })
+          const clientData = ref({
+            beforeMount,
+            ready: new Promise<void>(r => {
+              resolveAsyncSetup = r
+            }),
+          })
           const vaporChildCode = `
             <script vapor>
               import { onBeforeMount } from 'vue'
               const data = _data
               onBeforeMount(() => data.value.beforeMount())
-              await new Promise(r => setTimeout(r, 10))
+              await data.value.ready
             </script>
             <template><h1>Async component</h1></template>
           `
@@
           serverComponents.VaporChild = compile(
             vaporChildCode,
-            data,
+            serverData,
             serverComponents,
             {
               vapor: true,
               ssr: true,
             },
           )
           clientComponents.VaporChild = compile(
             vaporChildCode,
-            data,
+            clientData,
             clientComponents,
             {
               vapor: true,
               ssr: false,
             },
           )
@@
-          const serverApp = compile(appCode, data, serverComponents, {
+          const serverApp = compile(appCode, serverData, serverComponents, {
             vapor: false,
             ssr: true,
           })
@@
-          const clientApp = compile(appCode, data, clientComponents, {
+          const clientApp = compile(appCode, clientData, clientComponents, {
             vapor: false,
             ssr: false,
           })
@@
           app.mount(container)
-          await new Promise(r => setTimeout(r, 10))
+          resolveAsyncSetup()
+          await Promise.resolve()
           await nextTick()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/runtime-vapor/__tests__/hydration.spec.ts` around lines 4688 - 4747,
The test uses two fixed setTimeout(10) waits (one inside vaporChildCode and one
after app.mount) which is flaky; replace them with a deterministic async gate:
change the component code (vaporChildCode) to await a Promise you control (e.g.
a Deferred exposed via test-supplied data or a global like
window.__vaporChildGate), and in the test create/resolve that Deferred instead
of sleeping; then swap both await new Promise(r => setTimeout(r, 10)) usages to
await that gate (and keep the final await nextTick() for microtask flush).
Update places referencing vaporChildCode, appCode, app.mount and
runtimeVapor.vaporInteropPlugin to use the new gate so the test waits
deterministically.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/runtime-vapor/__tests__/hydration.spec.ts`:
- Around line 4688-4747: The test uses two fixed setTimeout(10) waits (one
inside vaporChildCode and one after app.mount) which is flaky; replace them with
a deterministic async gate: change the component code (vaporChildCode) to await
a Promise you control (e.g. a Deferred exposed via test-supplied data or a
global like window.__vaporChildGate), and in the test create/resolve that
Deferred instead of sleeping; then swap both await new Promise(r =>
setTimeout(r, 10)) usages to await that gate (and keep the final await
nextTick() for microtask flush). Update places referencing vaporChildCode,
appCode, app.mount and runtimeVapor.vaporInteropPlugin to use the new gate so
the test waits deterministically.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 522792bb-114b-4665-b0c2-c86e456e6495

📥 Commits

Reviewing files that changed from the base of the PR and between dfd106a and af4ab45.

📒 Files selected for processing (3)
  • packages/runtime-vapor/__tests__/hydration.spec.ts
  • packages/runtime-vapor/src/component.ts
  • packages/runtime-vapor/src/vdomInterop.ts

@edison1105 edison1105 force-pushed the edison/fix/hydrationBugs branch from 9c91951 to ffd5d16 Compare April 7, 2026 03:10
@edison1105 edison1105 changed the title fix(hydration): avoid double mount in vdom interop fix(hydration): avoid component double mount during hydration Apr 7, 2026
@edison1105 edison1105 merged commit 340af2c into minor Apr 7, 2026
17 checks passed
@edison1105 edison1105 deleted the edison/fix/hydrationBugs branch April 7, 2026 03:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: vapor related to vapor mode

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant