]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
chore: dont process text/comment node as dynamic
authordaiwei <daiwei521@126.com>
Wed, 23 Apr 2025 09:07:38 +0000 (17:07 +0800)
committerdaiwei <daiwei521@126.com>
Wed, 23 Apr 2025 09:43:47 +0000 (17:43 +0800)
packages/compiler-ssr/src/ssrCodegenTransform.ts
packages/runtime-core/src/hydration.ts
packages/runtime-vapor/__tests__/hydration.spec.ts

index c56dea164c0e569426df340fd5c5afb4d50f22c4..85118d91b3491eac13b0f925e927ded41ded4f28 100644 (file)
@@ -269,8 +269,10 @@ export function processChildrenAsStatement(
   return createBlockStatement(childContext.body)
 }
 
-const isStaticElement = (c: TemplateChildNode): boolean =>
-  c.type === NodeTypes.ELEMENT && c.tagType !== ElementTypes.COMPONENT
+const isStaticChildNode = (c: TemplateChildNode): boolean =>
+  (c.type === NodeTypes.ELEMENT && c.tagType !== ElementTypes.COMPONENT) ||
+  c.type === NodeTypes.TEXT ||
+  c.type === NodeTypes.COMMENT
 
 /**
  * Check if a node should be processed as dynamic.
@@ -289,11 +291,13 @@ function shouldProcessAsDynamic(
   node: TemplateChildNode,
 ): boolean {
   // 1. Must be a dynamic node type
-  if (isStaticElement(node)) return false
+  if (isStaticChildNode(node)) return false
   // 2. Must be inside a parent element
   if (!parent.tag) return false
 
-  const children = parent.children
+  const children = parent.children.filter(
+    child => !(child.type === NodeTypes.TEXT && !child.content.trim()),
+  )
   const len = children.length
   const index = children.indexOf(node)
 
@@ -301,7 +305,7 @@ function shouldProcessAsDynamic(
   let hasStaticPreviousSibling = false
   if (index > 0) {
     for (let i = index - 1; i >= 0; i--) {
-      if (isStaticElement(children[i])) {
+      if (isStaticChildNode(children[i])) {
         hasStaticPreviousSibling = true
         break
       }
@@ -313,7 +317,7 @@ function shouldProcessAsDynamic(
   let hasStaticNextSibling = false
   if (index > -1 && index < len - 1) {
     for (let i = index + 1; i < len; i++) {
-      if (isStaticElement(children[i])) {
+      if (isStaticChildNode(children[i])) {
         hasStaticNextSibling = true
         break
       }
@@ -321,19 +325,39 @@ function shouldProcessAsDynamic(
   }
   if (!hasStaticNextSibling) return false
 
-  // 5. Check for a consecutive dynamic sibling (immediately before or after)
-  let hasConsecutiveDynamicNodes = false
-  if (index > 0 && !isStaticElement(children[index - 1])) {
-    hasConsecutiveDynamicNodes = true
+  // 5. Calculate the number and location of continuous dynamic nodes
+  let dynamicNodeCount = 1 // The current node is counted as one
+  let prevDynamicCount = 0
+  let nextDynamicCount = 0
+
+  // Count consecutive dynamic nodes forward
+  for (let i = index - 1; i >= 0; i--) {
+    if (!isStaticChildNode(children[i])) {
+      prevDynamicCount++
+    } else {
+      break
+    }
+  }
+
+  // Count consecutive dynamic nodes backwards
+  for (let i = index + 1; i < len; i++) {
+    if (!isStaticChildNode(children[i])) {
+      nextDynamicCount++
+    } else {
+      break
+    }
+  }
+
+  dynamicNodeCount = 1 + prevDynamicCount + nextDynamicCount
+
+  // For two consecutive dynamic nodes, mark both as dynamic
+  if (dynamicNodeCount === 2) {
+    return prevDynamicCount > 0 || nextDynamicCount > 0
   }
-  if (
-    !hasConsecutiveDynamicNodes &&
-    index < len - 1 &&
-    !isStaticElement(children[index + 1])
-  ) {
-    hasConsecutiveDynamicNodes = true
+  // For three or more dynamic nodes, only mark the intermediate nodes as dynamic
+  else if (dynamicNodeCount >= 3) {
+    return prevDynamicCount > 0 && nextDynamicCount > 0
   }
 
-  // Only process as dynamic if all conditions are met
-  return hasConsecutiveDynamicNodes
+  return false
 }
index 94291d4091992aba7156a890254fcce1b2e9d60e..f0fc99ad2ff69604f1ae32bcf7641a95de593f14 100644 (file)
@@ -125,7 +125,7 @@ export function createHydrationFunctions(
 
   function nextSibling(node: Node) {
     let n = next(node)
-    // skip dynamic child anchor
+    // skip dynamic anchors
     if (n && isDynamicAnchor(n)) {
       n = next(n)
     }
index f9021ef938b9c970924c646979b7b76cf53a588b..0f039674ef578ce7cc4aca24627596e390e3b5fd 100644 (file)
@@ -334,13 +334,13 @@ describe('Vapor Mode hydration', () => {
       },
     )
     expect(container.innerHTML).toMatchInlineSnapshot(
-      `"<div><span></span><!--[[-->foo<!--]]--><!--[[--> <!--]]--><!--[[--> foo <!--]]--><!--[[--> <!--]]--><!--[[-->foo<!--]]--><span></span></div>"`,
+      `"<div><span></span>foo<!--[[--> foo <!--]]-->foo<span></span></div>"`,
     )
 
     data.value = 'bar'
     await nextTick()
     expect(container.innerHTML).toMatchInlineSnapshot(
-      `"<div><span></span><!--[[-->bar<!--]]--><!--[[--> <!--]]--><!--[[--> bar <!--]]--><!--[[--> <!--]]--><!--[[-->bar<!--]]--><span></span></div>"`,
+      `"<div><span></span>bar<!--[[--> bar <!--]]-->bar<span></span></div>"`,
     )
   })
 
@@ -439,13 +439,13 @@ describe('Vapor Mode hydration', () => {
       },
     )
     expect(container.innerHTML).toMatchInlineSnapshot(
-      `"<div><span></span><!--[[--><!--[--><div>foo</div>-foo<!--]--><!--]]--><!--[[--> <!--]]--><!--[[--> foo <!--]]--><!--[[--> <!--]]--><!--[[--><!--[--><div>foo</div>-foo<!--]--><!--]]--><span></span></div>"`,
+      `"<div><span></span><!--[--><div>foo</div>-foo<!--]--> <!--[[--> foo <!--]]--> <!--[--><div>foo</div>-foo<!--]--><span></span></div>"`,
     )
 
     data.value = 'bar'
     await nextTick()
     expect(container.innerHTML).toMatchInlineSnapshot(
-      `"<div><span></span><!--[[--><!--[--><div>bar</div>-bar<!--]--><!--]]--><!--[[--> <!--]]--><!--[[--> bar <!--]]--><!--[[--> <!--]]--><!--[[--><!--[--><div>bar</div>-bar<!--]--><!--]]--><span></span></div>"`,
+      `"<div><span></span><!--[--><div>bar</div>-bar<!--]--> <!--[[--> bar <!--]]--> <!--[--><div>bar</div>-bar<!--]--><span></span></div>"`,
     )
   })