]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix: handling v-for with non-hydration nodes
authordaiwei <daiwei521@126.com>
Tue, 14 Oct 2025 08:28:08 +0000 (16:28 +0800)
committerdaiwei <daiwei521@126.com>
Tue, 14 Oct 2025 08:28:08 +0000 (16:28 +0800)
packages/runtime-vapor/__tests__/hydration.spec.ts
packages/runtime-vapor/src/apiCreateFor.ts

index a20c6f3f55a7baab62f655218b224c5b8d80f194..7de42143f464cea8ba2cd9944236ed81252641e4 100644 (file)
@@ -1940,6 +1940,57 @@ describe('Vapor Mode hydration', () => {
         </div>"
       `)
     })
+
+    test('with non-hydration node', async () => {
+      const data = ref({ show: true, msg: 'foo' })
+      const { container } = await testHydration(
+        `<template>
+          <div>
+            <div v-for="item in 2">
+              <div>
+                <div v-if="data.show">{{ data.msg }}</div>
+              </div>
+              <span>non-hydration node</span>
+            </div>
+          </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><!--]-->
+        </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><!--]-->
+        </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><!--]-->
+        </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><!--]-->
+        </div>"
+      `)
+    })
   })
 
   describe('slots', () => {
index 6f7bd2dca5d00d680448adb00d02b4d71c63eb8e..bdeccce2a520cd9256f3bc4ecce4897cbf57233d 100644 (file)
@@ -132,18 +132,14 @@ export const createFor = (
 
     if (!isMounted) {
       isMounted = true
-      let prevNodes: Block
       for (let i = 0; i < newLength; i++) {
-        if (isHydrating && isComponent && i > 0) {
-          setCurrentHydrationNode(findLastChild(prevNodes!)!.nextSibling)
+        const nodes = mount(source, i).nodes
+        if (isHydrating) {
+          setCurrentHydrationNode(findLastChild(nodes!)!.nextSibling)
         }
-        prevNodes = mount(source, i).nodes
       }
 
       if (isHydrating) {
-        if (isComponent) {
-          setCurrentHydrationNode(findLastChild(prevNodes!)!.nextSibling)
-        }
         parentAnchor =
           newLength === 0
             ? currentHydrationNode!.nextSibling!