]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: refactor
authordaiwei <daiwei521@126.com>
Tue, 22 Apr 2025 13:20:44 +0000 (21:20 +0800)
committerdaiwei <daiwei521@126.com>
Tue, 22 Apr 2025 13:34:27 +0000 (21:34 +0800)
packages/compiler-ssr/__tests__/ssrElement.spec.ts
packages/compiler-ssr/src/ssrCodegenTransform.ts
packages/compiler-ssr/src/transforms/ssrTransformComponent.ts
packages/runtime-core/__tests__/hydration.spec.ts
packages/runtime-vapor/src/dom/hydration.ts

index 59f6b0ffbc9d13ad7906866a8feaa2b95aeb6947..fad89823893f99a08719544f5e458943f0dfcc17 100644 (file)
@@ -397,8 +397,8 @@ describe('ssr: element', () => {
     })
   })
 
-  describe('dynamic child anchor', () => {
-    test('with consecutive components', () => {
+  describe('dynamic anchor', () => {
+    test('consecutive components', () => {
       expect(
         getCompiledString(`
         <div>
@@ -409,14 +409,11 @@ describe('ssr: element', () => {
         </div>
         `),
       ).toMatchInlineSnapshot(`
-        "\`<div><div></div>\`)
-          _push("<!--[[-->")
+        "\`<div><div></div><!--[[-->\`)
           _push(_ssrRenderComponent(_component_Comp1, null, null, _parent))
-          _push("<!--]]-->")
-          _push("<!--[[-->")
+          _push(\`<!--]]--><!--[[-->\`)
           _push(_ssrRenderComponent(_component_Comp2, null, null, _parent))
-          _push("<!--]]-->")
-          _push(\`<div></div></div>\`"
+          _push(\`<!--]]--><div></div></div>\`"
       `)
     })
   })
index 536cbb5c1e97342f7d2aa2d04ddae688e1339736..c56dea164c0e569426df340fd5c5afb4d50f22c4 100644 (file)
@@ -157,13 +157,29 @@ export function processChildren(
   asFragment = false,
   disableNestedFragments = false,
   disableComment = false,
+  asDynamic = false,
 ): void {
+  if (asDynamic) {
+    context.pushStringPart(`<!--[[-->`)
+  }
   if (asFragment) {
     context.pushStringPart(`<!--[-->`)
   }
+
   const { children } = parent
   for (let i = 0; i < children.length; i++) {
     const child = children[i]
+    if (shouldProcessAsDynamic(parent, child)) {
+      processChildren(
+        { children: [child] },
+        context,
+        asFragment,
+        disableNestedFragments,
+        disableComment,
+        true,
+      )
+      continue
+    }
     switch (child.type) {
       case NodeTypes.ELEMENT:
         switch (child.tagType) {
@@ -237,6 +253,9 @@ export function processChildren(
   if (asFragment) {
     context.pushStringPart(`<!--]-->`)
   }
+  if (asDynamic) {
+    context.pushStringPart(`<!--]]-->`)
+  }
 }
 
 export function processChildrenAsStatement(
@@ -249,3 +268,72 @@ export function processChildrenAsStatement(
   processChildren(parent, childContext, asFragment)
   return createBlockStatement(childContext.body)
 }
+
+const isStaticElement = (c: TemplateChildNode): boolean =>
+  c.type === NodeTypes.ELEMENT && c.tagType !== ElementTypes.COMPONENT
+
+/**
+ * Check if a node should be processed as dynamic.
+ * This is primarily used in Vapor mode hydration to wrap dynamic parts
+ * with markers (`<!--[[-->` and `<!--]]-->`).
+ *
+ * <element>
+ *   <element/>  // Static previous sibling
+ *   <Comp/>     // Dynamic node (current)
+ *   <Comp/>     // Dynamic next sibling
+ *   <element/>  // Static next sibling
+ * </element>
+ */
+function shouldProcessAsDynamic(
+  parent: { tag?: string; children: TemplateChildNode[] },
+  node: TemplateChildNode,
+): boolean {
+  // 1. Must be a dynamic node type
+  if (isStaticElement(node)) return false
+  // 2. Must be inside a parent element
+  if (!parent.tag) return false
+
+  const children = parent.children
+  const len = children.length
+  const index = children.indexOf(node)
+
+  // 3. Check for a static previous sibling
+  let hasStaticPreviousSibling = false
+  if (index > 0) {
+    for (let i = index - 1; i >= 0; i--) {
+      if (isStaticElement(children[i])) {
+        hasStaticPreviousSibling = true
+        break
+      }
+    }
+  }
+  if (!hasStaticPreviousSibling) return false
+
+  // 4. Check for a static next sibling
+  let hasStaticNextSibling = false
+  if (index > -1 && index < len - 1) {
+    for (let i = index + 1; i < len; i++) {
+      if (isStaticElement(children[i])) {
+        hasStaticNextSibling = true
+        break
+      }
+    }
+  }
+  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
+  }
+  if (
+    !hasConsecutiveDynamicNodes &&
+    index < len - 1 &&
+    !isStaticElement(children[index + 1])
+  ) {
+    hasConsecutiveDynamicNodes = true
+  }
+
+  // Only process as dynamic if all conditions are met
+  return hasConsecutiveDynamicNodes
+}
index 48df3213377403b3044490c13b9f8518d4906b13..cad1ee8102897beab4c8990b99b43366c5d42655 100644 (file)
@@ -255,13 +255,6 @@ export function ssrProcessComponent(
       node.ssrCodegenNode.arguments.push(`_scopeId`)
     }
 
-    // `<!--[[-->` marks the start of the dynamic children
-    // Only used in Vapor hydration, VDOM hydration
-    // skips this marker.
-    const needDynamicAnchor = shouldAddDynamicAnchor(parent, node)
-    if (needDynamicAnchor) {
-      context.pushStatement(createCallExpression(`_push`, [`"<!--[[-->"`]))
-    }
     if (typeof component === 'string') {
       // static component
       context.pushStatement(
@@ -272,9 +265,6 @@ export function ssrProcessComponent(
       // the codegen node is a `renderVNode` call
       context.pushStatement(node.ssrCodegenNode)
     }
-    if (needDynamicAnchor) {
-      context.pushStatement(createCallExpression(`_push`, [`"<!--]]-->"`]))
-    }
   }
 }
 
@@ -394,58 +384,3 @@ function clone(v: any): any {
     return v
   }
 }
-
-function shouldAddDynamicAnchor(
-  parent: { tag?: string; children: TemplateChildNode[] },
-  node: TemplateChildNode,
-): boolean {
-  if (!parent.tag) return false
-
-  const children = parent.children
-  const len = children.length
-  const index = children.indexOf(node)
-
-  const isStaticElement = (c: TemplateChildNode): boolean =>
-    c.type === NodeTypes.ELEMENT && c.tagType !== ElementTypes.COMPONENT
-
-  let hasStaticPreviousSibling = false
-  if (index > 0) {
-    for (let i = index - 1; i >= 0; i--) {
-      if (isStaticElement(children[i])) {
-        hasStaticPreviousSibling = true
-        break
-      }
-    }
-  }
-
-  let hasStaticNextSibling = false
-  if (hasStaticPreviousSibling && index > -1 && index < len - 1) {
-    for (let i = index + 1; i < len; i++) {
-      if (isStaticElement(children[i])) {
-        hasStaticNextSibling = true
-        break
-      }
-    }
-  }
-
-  let hasConsecutiveDynamicNodes = false
-  if (index > 0 && index < len - 1) {
-    if (index > 0 && !isStaticElement(children[index - 1])) {
-      hasConsecutiveDynamicNodes = true
-    }
-
-    if (
-      !hasConsecutiveDynamicNodes &&
-      index < len - 1 &&
-      !isStaticElement(children[index + 1])
-    ) {
-      hasConsecutiveDynamicNodes = true
-    }
-  }
-
-  return (
-    hasStaticPreviousSibling &&
-    hasStaticNextSibling &&
-    hasConsecutiveDynamicNodes
-  )
-}
index a6a8960fb1f19342dce14498e726b1045f38db34..23b223526ff6e8b461fe3eccd3c1e65b96ebc136 100644 (file)
@@ -1843,8 +1843,8 @@ describe('SSR hydration', () => {
     }
   })
 
-  describe('dynamic child anchor', () => {
-    test('with consecutive components', () => {
+  describe('dynamic anchor', () => {
+    test('consecutive components', () => {
       const Comp = {
         render() {
           return createTextVNode('foo')
index a3b0eecb4ba4fc78876404201e32f32f48a8b4bc..30560801e560da77c21977b3abd5eb5799d1d069 100644 (file)
@@ -75,9 +75,9 @@ function locateHydrationNodeImpl() {
   // prepend / firstChild
   if (insertionAnchor === 0) {
     node = child(insertionParent!)
-  } else if (insertionParent && insertionAnchor) {
-    // dynamic child anchor `<!--[[-->`
-    if (insertionAnchor && isDynamicStart(insertionAnchor)) {
+  } else if (insertionAnchor) {
+    // dynamic anchor `<!--[[-->`
+    if (isDynamicStart(insertionAnchor)) {
       const anchor = (insertionParent!.$lds = insertionParent!.$lds
         ? // continuous dynamic children, the next dynamic start must exist
           locateNextDynamicStart(insertionParent!.$lds)!
@@ -87,11 +87,7 @@ function locateHydrationNodeImpl() {
       node = insertionAnchor
     }
   } else {
-    node = insertionAnchor
-      ? insertionAnchor.previousSibling
-      : insertionParent
-        ? insertionParent.lastChild
-        : currentHydrationNode
+    node = insertionParent ? insertionParent.lastChild : currentHydrationNode
     if (node && isComment(node, ']')) {
       // fragment backward search
       if (node.$fs) {