]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor: add enableHydrationNodeLookup and disableHydrationNodeLookup functions...
authordaiwei <daiwei521@126.com>
Wed, 23 Apr 2025 07:43:58 +0000 (15:43 +0800)
committerdaiwei <daiwei521@126.com>
Wed, 23 Apr 2025 08:15:49 +0000 (16:15 +0800)
packages/runtime-core/src/hydration.ts
packages/runtime-core/src/index.ts
packages/runtime-vapor/src/dom/hydration.ts
packages/runtime-vapor/src/dom/node.ts
packages/runtime-vapor/src/insertionState.ts

index 13f900a9482c5e0abaaa5005471e776ef24197c8..94291d4091992aba7156a890254fcce1b2e9d60e 100644 (file)
@@ -84,6 +84,10 @@ const getContainerType = (
   return undefined
 }
 
+export function isDynamicAnchor(node: Node): boolean {
+  return isComment(node) && (node.data === '[[' || node.data === ']]')
+}
+
 export const isComment = (node: Node): node is Comment =>
   node.nodeType === DOMNodeTypes.COMMENT
 
@@ -119,10 +123,6 @@ export function createHydrationFunctions(
     },
   } = rendererInternals
 
-  function isDynamicAnchor(node: Node): boolean {
-    return isComment(node) && (node.data === '[[' || node.data === ']]')
-  }
-
   function nextSibling(node: Node) {
     let n = next(node)
     // skip dynamic child anchor
index e309554f2f6c3edd517c9b736d06ee1c72594698..4c31ff510e034eaea67f4e315b46ce867a5290f3 100644 (file)
@@ -557,3 +557,7 @@ export { startMeasure, endMeasure } from './profiling'
  * @internal
  */
 export { initFeatureFlags } from './featureFlags'
+/**
+ * @internal
+ */
+export { isDynamicAnchor } from './hydration'
index 37568b136c28056e962fdfe92c0d85f9b6decfec..55dd9853294e54ad706c6281509a7584187f3c01 100644 (file)
@@ -1,12 +1,16 @@
 import { warn } from '@vue/runtime-dom'
 import {
-  type Anchor,
   insertionAnchor,
   insertionParent,
   resetInsertionState,
   setInsertionState,
 } from '../insertionState'
-import { child, next } from './node'
+import {
+  child,
+  disableHydrationNodeLookup,
+  enableHydrationNodeLookup,
+  next,
+} from './node'
 
 export let isHydrating = false
 export let currentHydrationNode: Node | null = null
@@ -25,18 +29,26 @@ export function withHydration(container: ParentNode, fn: () => void): void {
     ;(Comment.prototype as any).$fs = undefined
     isOptimized = true
   }
+  enableHydrationNodeLookup()
   isHydrating = true
   setInsertionState(container, 0)
   const res = fn()
   resetInsertionState()
   currentHydrationNode = null
   isHydrating = false
+  disableHydrationNodeLookup()
   return res
 }
 
 export let adoptTemplate: (node: Node, template: string) => Node | null
 export let locateHydrationNode: () => void
 
+type Anchor = Comment & {
+  // cached matching fragment start to avoid repeated traversal
+  // on nested fragments
+  $fs?: Anchor
+}
+
 export const isComment = (node: Node, data: string): node is Anchor =>
   node.nodeType === 8 && (node as Comment).data === data
 
@@ -120,10 +132,6 @@ function locateHydrationNodeImpl() {
   currentHydrationNode = node
 }
 
-export function isDynamicAnchor(node: Node): node is Comment {
-  return isComment(node, '[[') || isComment(node, ']]')
-}
-
 export function isEmptyText(node: Node): node is Text {
   return node.nodeType === 3 && !(node as Text).data.trim()
 }
index 3a1e283b1d57fd0b2badfcf3d9c18bea86ea6601..91fc2714fa3da58981ba2b504314d3fd217d056f 100644 (file)
@@ -1,10 +1,5 @@
-import {
-  isComment,
-  isDynamicAnchor,
-  isEmptyText,
-  isHydrating,
-  locateEndAnchor,
-} from './hydration'
+import { isDynamicAnchor } from '@vue/runtime-dom'
+import { isComment, isEmptyText, locateEndAnchor } from './hydration'
 
 /*! #__NO_SIDE_EFFECTS__ */
 export function createTextNode(value = ''): Text {
@@ -27,9 +22,12 @@ export function child(node: ParentNode): Node {
 }
 
 /*! #__NO_SIDE_EFFECTS__ */
-export function nthChild(node: Node, i: number): Node {
-  if (!isHydrating) return node.childNodes[i]
+export function _nthChild(node: Node, i: number): Node {
+  return node.childNodes[i]
+}
 
+/*! #__NO_SIDE_EFFECTS__ */
+export function __nthChild(node: Node, i: number): Node {
   let n = node.firstChild!
   for (let start = 0; start < i; start++) {
     n = next(n) as ChildNode
@@ -38,9 +36,12 @@ export function nthChild(node: Node, i: number): Node {
 }
 
 /*! #__NO_SIDE_EFFECTS__ */
-export function next(node: Node): Node {
-  if (!isHydrating) return node.nextSibling!
+function _next(node: Node): Node {
+  return node.nextSibling!
+}
 
+/*! #__NO_SIDE_EFFECTS__ */
+function __next(node: Node): Node {
   // process fragment as a single node
   if (node && isComment(node, '[')) {
     node = locateEndAnchor(node)!
@@ -53,3 +54,46 @@ export function next(node: Node): Node {
   }
   return n
 }
+
+type NextFn = (node: Node) => Node
+type NthChildFn = (node: Node, i: number) => Node
+
+interface DelegatedNextFunction extends NextFn {
+  impl: NextFn
+}
+interface DelegatedNthChildFunction extends NthChildFn {
+  impl: NthChildFn
+}
+
+/*! #__NO_SIDE_EFFECTS__ */
+export const next: DelegatedNextFunction = node => {
+  return next.impl(node)
+}
+next.impl = _next
+
+/*! #__NO_SIDE_EFFECTS__ */
+export const nthChild: DelegatedNthChildFunction = (node, i) => {
+  return nthChild.impl(node, i)
+}
+nthChild.impl = _nthChild
+
+// During hydration, there might be differences between the server-rendered (SSR)
+// HTML and the client-side template.
+// For example, a dynamic node `<!>` in the template might be rendered as a
+// `Fragment` (`<!--[-->...<!--]-->`) in the SSR output.
+// The content of the `Fragment` affects the lookup results of the `next` and
+// `nthChild` functions.
+// To ensure the hydration process correctly finds nodes, we need to treat the
+// `Fragment` as a single node.
+// Therefore, during hydration, we need to temporarily switch the implementations
+// of `next` and `nthChild`. After hydration is complete, their implementations
+// are restored to the original versions.
+export function enableHydrationNodeLookup(): void {
+  next.impl = __next
+  nthChild.impl = __nthChild
+}
+
+export function disableHydrationNodeLookup(): void {
+  next.impl = _next
+  nthChild.impl = _nthChild
+}
index 8280b65c281ee82b3af447fe6a66c40509d83248..c8c7ffbcd1de3b1000cbc9fa62f50ee9c8c3a0b3 100644 (file)
@@ -1,10 +1,5 @@
-export let insertionParent:
-  | (ParentNode & {
-      // cached the last dynamic start anchor
-      $lds?: Anchor
-    })
-  | undefined
-export let insertionAnchor: Node | 0 | undefined | null
+export let insertionParent: ParentNode | undefined
+export let insertionAnchor: Node | 0 | undefined
 
 /**
  * This function is called before a block type that requires insertion
@@ -19,13 +14,3 @@ export function setInsertionState(parent: ParentNode, anchor?: Node | 0): void {
 export function resetInsertionState(): void {
   insertionParent = insertionAnchor = undefined
 }
-
-export function setInsertionAnchor(anchor: Node | null): void {
-  insertionAnchor = anchor
-}
-
-export type Anchor = Comment & {
-  // cached matching fragment start to avoid repeated traversal
-  // on nested fragments
-  $fs?: Anchor
-}