]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor(runtime-vapor): extract DynamicFragment hydration logic for treeshaking
authordaiwei <daiwei521@126.com>
Fri, 26 Dec 2025 01:38:51 +0000 (09:38 +0800)
committeredison <daiwei521@126.com>
Fri, 26 Dec 2025 05:37:59 +0000 (13:37 +0800)
packages/runtime-vapor/src/dom/hydration.ts
packages/runtime-vapor/src/fragment.ts

index 5ad6f1e3070a6b6355376e448c2f7ca9f9037514..8c1ae81af915ae0dbf74ba18bbc923771348bd60 100644 (file)
@@ -1,4 +1,9 @@
-import { MismatchTypes, isMismatchAllowed, warn } from '@vue/runtime-dom'
+import {
+  MismatchTypes,
+  isMismatchAllowed,
+  queuePostFlushCb,
+  warn,
+} from '@vue/runtime-dom'
 import {
   type ChildItem,
   insertionAnchor,
@@ -9,6 +14,7 @@ import {
 import {
   _child,
   _next,
+  createComment,
   createElement,
   createTextNode,
   disableHydrationNodeLookup,
@@ -16,12 +22,14 @@ import {
   locateChildByLogicalIndex,
   parentNode,
 } from './node'
-import { remove } from '../block'
+import { findBlockNode, remove } from '../block'
 
 const isHydratingStack = [] as boolean[]
 export let isHydrating = false
 export let currentHydrationNode: Node | null = null
 
+let _hydrateDynamicFragment: ((frag: any, isEmpty: boolean) => void) | undefined
+
 function pushIsHydrating(value: boolean): void {
   isHydratingStack.push((isHydrating = value))
 }
@@ -50,6 +58,7 @@ function performHydration<T>(
   if (!isOptimized) {
     adoptTemplate = adoptTemplateImpl
     locateHydrationNode = locateHydrationNodeImpl
+    _hydrateDynamicFragment = hydrateDynamicFragmentImpl
     // optimize anchor cache lookup
     ;(Comment.prototype as any).$fe = undefined
     ;(Node.prototype as any).$pns = undefined
@@ -307,3 +316,62 @@ export function removeFragmentNodes(node: Node, endAnchor?: Node): void {
     }
   }
 }
+
+export function hydrateDynamicFragment(frag: any, isEmpty: boolean): void {
+  _hydrateDynamicFragment && _hydrateDynamicFragment(frag, isEmpty)
+}
+
+// Hydrate implementation for DynamicFragment
+function hydrateDynamicFragmentImpl(frag: any, isEmpty: boolean): void {
+  // avoid repeated hydration during fallback rendering
+  if (frag.anchor) return
+
+  if (frag.anchorLabel === 'if') {
+    // reuse the empty comment node as the anchor for empty if
+    // e.g. `<div v-if="false"></div>` -> `<!---->`
+    if (isEmpty) {
+      frag.anchor = locateFragmentEndAnchor('')!
+      if (__DEV__ && !frag.anchor) {
+        throw new Error(
+          'Failed to locate if anchor. this is likely a Vue internal bug.',
+        )
+      } else {
+        if (__DEV__) {
+          ;(frag.anchor as Comment).data = frag.anchorLabel
+        }
+        return
+      }
+    }
+  } else if (frag.anchorLabel === 'slot') {
+    // reuse the empty comment node for empty slot
+    // e.g. `<slot v-if="false"></slot>`
+    if (isEmpty && isComment(currentHydrationNode!, '')) {
+      frag.anchor = currentHydrationNode!
+      if (__DEV__) {
+        ;(frag.anchor as Comment).data = frag.anchorLabel!
+      }
+      return
+    }
+
+    // reuse the vdom fragment end anchor
+    frag.anchor = locateFragmentEndAnchor()!
+    if (__DEV__ && !frag.anchor) {
+      throw new Error(
+        'Failed to locate slot anchor. this is likely a Vue internal bug.',
+      )
+    } else {
+      return
+    }
+  }
+
+  const { parentNode: pn, nextNode } = findBlockNode(frag.nodes)!
+  // create an anchor
+  queuePostFlushCb(() => {
+    pn!.insertBefore(
+      (frag.anchor = __DEV__
+        ? createComment(frag.anchorLabel!)
+        : createTextNode()),
+      nextNode,
+    )
+  })
+}
index 28df9a27be0fba5492401248dd99f28644ce8d0c..9f0368734a0b12085f6ff18b59023f9843568e39 100644 (file)
@@ -7,7 +7,6 @@ import {
   type VaporTransitionHooks,
   applyTransitionHooks,
   applyTransitionLeaveHooks,
-  findBlockNode,
   insert,
   isValidBlock,
   remove,
@@ -17,17 +16,14 @@ import {
   type TransitionHooks,
   type VNode,
   currentInstance,
-  queuePostFlushCb,
   setCurrentInstance,
   warnExtraneousAttributes,
 } from '@vue/runtime-dom'
 import { type VaporComponentInstance, applyFallthroughProps } from './component'
 import type { NodeRef } from './apiTemplateRef'
 import {
-  currentHydrationNode,
-  isComment,
+  hydrateDynamicFragment,
   isHydrating,
-  locateFragmentEndAnchor,
   locateHydrationNode,
 } from './dom/hydration'
 import { isArray } from '@vue/shared'
@@ -278,57 +274,7 @@ export class DynamicFragment extends VaporFragment {
   }
 
   hydrate = (isEmpty = false): void => {
-    // avoid repeated hydration during fallback rendering
-    if (this.anchor) return
-
-    if (this.anchorLabel === 'if') {
-      // reuse the empty comment node as the anchor for empty if
-      // e.g. `<div v-if="false"></div>` -> `<!---->`
-      if (isEmpty) {
-        this.anchor = locateFragmentEndAnchor('')!
-        if (__DEV__ && !this.anchor) {
-          throw new Error(
-            'Failed to locate if anchor. this is likely a Vue internal bug.',
-          )
-        } else {
-          if (__DEV__) {
-            ;(this.anchor as Comment).data = this.anchorLabel
-          }
-          return
-        }
-      }
-    } else if (this.anchorLabel === 'slot') {
-      // reuse the empty comment node for empty slot
-      // e.g. `<slot v-if="false"></slot>`
-      if (isEmpty && isComment(currentHydrationNode!, '')) {
-        this.anchor = currentHydrationNode!
-        if (__DEV__) {
-          ;(this.anchor as Comment).data = this.anchorLabel!
-        }
-        return
-      }
-
-      // reuse the vdom fragment end anchor
-      this.anchor = locateFragmentEndAnchor()!
-      if (__DEV__ && !this.anchor) {
-        throw new Error(
-          'Failed to locate slot anchor. this is likely a Vue internal bug.',
-        )
-      } else {
-        return
-      }
-    }
-
-    const { parentNode, nextNode } = findBlockNode(this.nodes)!
-    // create an anchor
-    queuePostFlushCb(() => {
-      parentNode!.insertBefore(
-        (this.anchor = __DEV__
-          ? createComment(this.anchorLabel!)
-          : createTextNode()),
-        nextNode,
-      )
-    })
+    hydrateDynamicFragment(this, isEmpty)
   }
 }