]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(hydration): handle consecutive if node
authordaiwei <daiwei521@126.com>
Wed, 30 Jul 2025 06:39:56 +0000 (14:39 +0800)
committerdaiwei <daiwei521@126.com>
Wed, 30 Jul 2025 06:53:19 +0000 (14:53 +0800)
packages/runtime-vapor/__tests__/hydration.spec.ts
packages/runtime-vapor/src/dom/hydration.ts
packages/runtime-vapor/src/fragment.ts

index 54381d7e688d5c25d21fd4dae3b8e2738a1d5741..16b08893434f2b552b0345776f9b2c226993132b 100644 (file)
@@ -1269,6 +1269,26 @@ describe('Vapor Mode hydration', () => {
       expect(container.innerHTML).toBe(`<!--${anchorLabel}-->`)
     })
 
+    test('consecutive if node', async () => {
+      const data = ref(true)
+      const { container } = await testHydration(
+        `<template>
+          <components.Child v-if="data"/>
+        </template>`,
+        { Child: `<template><div v-if="data">foo</div></template>` },
+        data,
+      )
+      expect(container.innerHTML).toBe(`<div>foo</div><!--if--><!--if-->`)
+
+      data.value = false
+      await nextTick()
+      expect(container.innerHTML).toBe(`<!--if-->`)
+
+      data.value = true
+      await nextTick()
+      expect(container.innerHTML).toBe(`<div>foo</div><!--if--><!--if-->`)
+    })
+
     test('v-if/else-if/else chain on component - switch branches', async () => {
       const data = ref('a')
       const { container } = await testHydration(
index 0d4a118f19afe1e83f029aa74a0740b5db509439..2802b2860d55e0e3a206da771d511376f791afaa 100644 (file)
@@ -20,6 +20,10 @@ export function setCurrentHydrationNode(node: Node | null): void {
   currentHydrationNode = node
 }
 
+export function advanceHydrationNode(node: Node): void {
+  setCurrentHydrationNode(node.nextSibling || node.parentNode)
+}
+
 let isOptimized = false
 
 function performHydration<T>(
@@ -96,7 +100,7 @@ function adoptTemplateImpl(node: Node, template: string): Node | null {
     }
   }
 
-  currentHydrationNode = __next(node)
+  advanceHydrationNode(node)
   return node
 }
 
@@ -194,12 +198,12 @@ export function isNonHydrationNode(node: Node): boolean {
 export function locateVaporFragmentAnchor(
   node: Node,
   anchorLabel: string,
-): Comment | undefined {
-  let n = node.nextSibling
-  while (n) {
-    if (isComment(n, anchorLabel)) return n
-    n = n.nextSibling
+): Comment | null {
+  while (node) {
+    if (isComment(node, anchorLabel)) return node
+    node = node.nextSibling!
   }
+  return null
 }
 
 export function isEmptyTextNode(node: Node): node is Text {
index 50ce18bdb3ac4fd505eef84c5fe3280ad46741cf..2f6d3b42ad64ce1b7c8a9709f3cf52f5c03569f1 100644 (file)
@@ -11,12 +11,12 @@ import {
 } from './block'
 import type { TransitionHooks } from '@vue/runtime-dom'
 import {
+  advanceHydrationNode,
   currentHydrationNode,
   isComment,
   isHydrating,
   locateHydrationNode,
   locateVaporFragmentAnchor,
-  setCurrentHydrationNode,
 } from './dom/hydration'
 import {
   applyTransitionHooks,
@@ -60,12 +60,13 @@ export class DynamicFragment extends VaporFragment {
    */
   forwarded?: boolean
   teardown?: () => void
+  anchorLabel?: string
 
   constructor(anchorLabel?: string) {
     super([])
     if (isHydrating) {
       locateHydrationNode(anchorLabel === 'slot')
-      this.hydrate(anchorLabel!)
+      this.anchorLabel = anchorLabel
     } else {
       this.anchor =
         __DEV__ && anchorLabel ? createComment(anchorLabel) : createTextNode()
@@ -74,12 +75,13 @@ export class DynamicFragment extends VaporFragment {
 
   update(render?: BlockFn, key: any = render): void {
     if (key === this.current) {
+      if (isHydrating) this.hydrate(this.anchorLabel!)
       return
     }
     this.current = key
 
     const prevSub = setActiveSub()
-    const parent = this.anchor.parentNode
+    const parent = isHydrating ? null : this.anchor.parentNode
     const transition = this.$transition
     const renderBranch = () => {
       if (render) {
@@ -137,10 +139,8 @@ export class DynamicFragment extends VaporFragment {
       }
     }
 
-    if (isHydrating) {
-      setCurrentHydrationNode(this.anchor.nextSibling)
-    }
     setActiveSub(prevSub)
+    if (isHydrating) this.hydrate(this.anchorLabel!)
   }
 
   hydrate(label: string): void {
@@ -161,6 +161,7 @@ export class DynamicFragment extends VaporFragment {
         throw new Error(`${label} fragment anchor node was not found.`)
       }
     }
+    advanceHydrationNode(this.anchor)
   }
 }