]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix: handling v-for on component with non-hydration node
authordaiwei <daiwei521@126.com>
Tue, 16 Sep 2025 14:49:02 +0000 (22:49 +0800)
committerdaiwei <daiwei521@126.com>
Wed, 17 Sep 2025 01:04:22 +0000 (09:04 +0800)
packages/runtime-vapor/__tests__/hydration.spec.ts
packages/runtime-vapor/src/apiCreateFor.ts
packages/runtime-vapor/src/block.ts

index 999caf97c8a30abaa1d1946a86374fbbd3a688cc..1eb43e0bbaecae8e8833cb9690813a1ac37a93a7 100644 (file)
@@ -1746,8 +1746,8 @@ describe('Vapor Mode hydration', () => {
         <!--[-->
         <!--[--><span>a</span><!--]-->
         <!--[--><span>b</span><!--]-->
-        <!--[--><span>c</span><!--for--><!--]-->
-        </div>"
+        <!--[--><span>c</span><!--]-->
+        <!--for--></div>"
       `,
       )
 
@@ -1759,8 +1759,8 @@ describe('Vapor Mode hydration', () => {
         <!--[-->
         <!--[--><span>a</span><!--]-->
         <!--[--><span>b</span><!--]-->
-        <!--[--><span>c</span><span>d</span><!--slot--><!--for--><!--]-->
-        </div>"
+        <!--[--><span>c</span><!--]-->
+        <span>d</span><!--slot--><!--for--></div>"
       `,
       )
     })
@@ -1801,6 +1801,57 @@ describe('Vapor Mode hydration', () => {
       `,
       )
     })
+
+    test('on component with non-hydration node', async () => {
+      const data = ref({ show: true, msg: 'foo' })
+      const { container } = await testHydration(
+        `<template>
+          <div>
+            <components.Child v-for="item in 2" :key="item"/>
+          </div>
+        </template>`,
+        {
+          Child: `<template>
+            <div>
+              <div>
+                <div v-if="data.show">{{ data.msg }}</div>
+              </div>
+              <span>non-hydration node</span>
+            </div>
+          </template>`,
+        },
+        data,
+      )
+      expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
+        `
+        "<div>
+        <!--[--><div><div><div>foo</div><!--if--></div><span>non-hydration node</span></div><div><div><div>foo</div><!--if--></div><span>non-hydration node</span></div><!--for--></div>"
+      `,
+      )
+
+      data.value.msg = 'bar'
+      await nextTick()
+      expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(
+        `
+        "<div>
+        <!--[--><div><div><div>bar</div><!--if--></div><span>non-hydration node</span></div><div><div><div>bar</div><!--if--></div><span>non-hydration node</span></div><!--for--></div>"
+      `,
+      )
+
+      data.value.show = false
+      await nextTick()
+      expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(`
+        "<div>
+        <!--[--><div><div><!--if--></div><span>non-hydration node</span></div><div><div><!--if--></div><span>non-hydration node</span></div><!--for--></div>"
+      `)
+
+      data.value.show = true
+      await nextTick()
+      expect(formatHtml(container.innerHTML)).toMatchInlineSnapshot(`
+        "<div>
+        <!--[--><div><div><div>bar</div><!--if--></div><span>non-hydration node</span></div><div><div><div>bar</div><!--if--></div><span>non-hydration node</span></div><!--for--></div>"
+      `)
+    })
   })
 
   describe('slots', () => {
index 4e88afe8f4f3d8accd67b396accf0abf55e0aff5..7fdc312146b408acac3baf996fff7df176199444 100644 (file)
@@ -30,8 +30,9 @@ import {
   isHydrating,
   locateFragmentEndAnchor,
   locateHydrationNode,
+  setCurrentHydrationNode,
 } from './dom/hydration'
-import { ForFragment, VaporFragment } from './fragment'
+import { ForFragment, VaporFragment, findLastChild } from './fragment'
 import {
   insertionAnchor,
   insertionParent,
@@ -130,10 +131,20 @@ export const createFor = (
     if (!isMounted) {
       isMounted = true
       for (let i = 0; i < newLength; i++) {
+        if (isHydrating && isComponent && i > 0) {
+          setCurrentHydrationNode(
+            findLastChild(newBlocks[i - 1].nodes)!.nextSibling,
+          )
+        }
         mount(source, i)
       }
 
       if (isHydrating) {
+        if (isComponent) {
+          setCurrentHydrationNode(
+            findLastChild(newBlocks[newLength - 1].nodes)!.nextSibling,
+          )
+        }
         parentAnchor = locateFragmentEndAnchor()!
         if (__DEV__) {
           if (!parentAnchor) {
index aeac078ae20c16158adeae0060aae8ec3e2ddcd7..16e18e9aa1355d7a6f480e4b5ce2523a3c5e8c66 100644 (file)
@@ -157,7 +157,7 @@ export function normalizeAnchor(node: Block): Node | undefined {
   if (node && node instanceof Node) {
     return node
   } else if (isArray(node)) {
-    return normalizeAnchor(node[0])
+    return normalizeAnchor(node[node.length - 1])
   } else if (isVaporComponent(node)) {
     return normalizeAnchor(node.block!)
   } else {