Skip to content

Commit 25b8fbe

Browse files
committed
refactor: add enableHydrationNodeLookup and disableHydrationNodeLookup functions for node handling
1 parent 1248172 commit 25b8fbe

File tree

5 files changed

+79
-38
lines changed

5 files changed

+79
-38
lines changed

packages/runtime-core/src/hydration.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ const getContainerType = (
8484
return undefined
8585
}
8686

87+
export function isDynamicAnchor(node: Node): boolean {
88+
return isComment(node) && (node.data === '[[' || node.data === ']]')
89+
}
90+
8791
export const isComment = (node: Node): node is Comment =>
8892
node.nodeType === DOMNodeTypes.COMMENT
8993

@@ -119,10 +123,6 @@ export function createHydrationFunctions(
119123
},
120124
} = rendererInternals
121125

122-
function isDynamicAnchor(node: Node): boolean {
123-
return isComment(node) && (node.data === '[[' || node.data === ']]')
124-
}
125-
126126
function nextSibling(node: Node) {
127127
let n = next(node)
128128
// skip dynamic child anchor

packages/runtime-core/src/index.ts

+4
Original file line numberDiff line numberDiff line change
@@ -557,3 +557,7 @@ export { startMeasure, endMeasure } from './profiling'
557557
* @internal
558558
*/
559559
export { initFeatureFlags } from './featureFlags'
560+
/**
561+
* @internal
562+
*/
563+
export { isDynamicAnchor } from './hydration'

packages/runtime-vapor/src/dom/hydration.ts

+14-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
import { warn } from '@vue/runtime-dom'
22
import {
3-
type Anchor,
43
insertionAnchor,
54
insertionParent,
65
resetInsertionState,
76
setInsertionState,
87
} from '../insertionState'
9-
import { child, next } from './node'
8+
import {
9+
child,
10+
disableHydrationNodeLookup,
11+
enableHydrationNodeLookup,
12+
next,
13+
} from './node'
1014

1115
export let isHydrating = false
1216
export let currentHydrationNode: Node | null = null
@@ -25,18 +29,26 @@ export function withHydration(container: ParentNode, fn: () => void): void {
2529
;(Comment.prototype as any).$fs = undefined
2630
isOptimized = true
2731
}
32+
enableHydrationNodeLookup()
2833
isHydrating = true
2934
setInsertionState(container, 0)
3035
const res = fn()
3136
resetInsertionState()
3237
currentHydrationNode = null
3338
isHydrating = false
39+
disableHydrationNodeLookup()
3440
return res
3541
}
3642

3743
export let adoptTemplate: (node: Node, template: string) => Node | null
3844
export let locateHydrationNode: () => void
3945

46+
type Anchor = Comment & {
47+
// cached matching fragment start to avoid repeated traversal
48+
// on nested fragments
49+
$fs?: Anchor
50+
}
51+
4052
export const isComment = (node: Node, data: string): node is Anchor =>
4153
node.nodeType === 8 && (node as Comment).data === data
4254

@@ -120,10 +132,6 @@ function locateHydrationNodeImpl() {
120132
currentHydrationNode = node
121133
}
122134

123-
export function isDynamicAnchor(node: Node): node is Comment {
124-
return isComment(node, '[[') || isComment(node, ']]')
125-
}
126-
127135
export function isEmptyText(node: Node): node is Text {
128136
return node.nodeType === 3 && !(node as Text).data.trim()
129137
}

packages/runtime-vapor/src/dom/node.ts

+55-11
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
1-
import {
2-
isComment,
3-
isDynamicAnchor,
4-
isEmptyText,
5-
isHydrating,
6-
locateEndAnchor,
7-
} from './hydration'
1+
import { isDynamicAnchor } from '@vue/runtime-dom'
2+
import { isComment, isEmptyText, locateEndAnchor } from './hydration'
83

94
/*! #__NO_SIDE_EFFECTS__ */
105
export function createTextNode(value = ''): Text {
@@ -27,9 +22,12 @@ export function child(node: ParentNode): Node {
2722
}
2823

2924
/*! #__NO_SIDE_EFFECTS__ */
30-
export function nthChild(node: Node, i: number): Node {
31-
if (!isHydrating) return node.childNodes[i]
25+
export function _nthChild(node: Node, i: number): Node {
26+
return node.childNodes[i]
27+
}
3228

29+
/*! #__NO_SIDE_EFFECTS__ */
30+
export function __nthChild(node: Node, i: number): Node {
3331
let n = node.firstChild!
3432
for (let start = 0; start < i; start++) {
3533
n = next(n) as ChildNode
@@ -38,9 +36,12 @@ export function nthChild(node: Node, i: number): Node {
3836
}
3937

4038
/*! #__NO_SIDE_EFFECTS__ */
41-
export function next(node: Node): Node {
42-
if (!isHydrating) return node.nextSibling!
39+
function _next(node: Node): Node {
40+
return node.nextSibling!
41+
}
4342

43+
/*! #__NO_SIDE_EFFECTS__ */
44+
function __next(node: Node): Node {
4445
// process fragment as a single node
4546
if (node && isComment(node, '[')) {
4647
node = locateEndAnchor(node)!
@@ -53,3 +54,46 @@ export function next(node: Node): Node {
5354
}
5455
return n
5556
}
57+
58+
type NextFn = (node: Node) => Node
59+
type NthChildFn = (node: Node, i: number) => Node
60+
61+
interface DelegatedNextFunction extends NextFn {
62+
impl: NextFn
63+
}
64+
interface DelegatedNthChildFunction extends NthChildFn {
65+
impl: NthChildFn
66+
}
67+
68+
/*! #__NO_SIDE_EFFECTS__ */
69+
export const next: DelegatedNextFunction = node => {
70+
return next.impl(node)
71+
}
72+
next.impl = _next
73+
74+
/*! #__NO_SIDE_EFFECTS__ */
75+
export const nthChild: DelegatedNthChildFunction = (node, i) => {
76+
return nthChild.impl(node, i)
77+
}
78+
nthChild.impl = _nthChild
79+
80+
// During hydration, there might be differences between the server-rendered (SSR)
81+
// HTML and the client-side template.
82+
// For example, a dynamic node `<!>` in the template might be rendered as a
83+
// `Fragment` (`<!--[-->...<!--]-->`) in the SSR output.
84+
// The content of the `Fragment` affects the lookup results of the `next` and
85+
// `nthChild` functions.
86+
// To ensure the hydration process correctly finds nodes, we need to treat the
87+
// `Fragment` as a single node.
88+
// Therefore, during hydration, we need to temporarily switch the implementations
89+
// of `next` and `nthChild`. After hydration is complete, their implementations
90+
// are restored to the original versions.
91+
export function enableHydrationNodeLookup(): void {
92+
next.impl = __next
93+
nthChild.impl = __nthChild
94+
}
95+
96+
export function disableHydrationNodeLookup(): void {
97+
next.impl = _next
98+
nthChild.impl = _nthChild
99+
}
+2-17
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
1-
export let insertionParent:
2-
| (ParentNode & {
3-
// cached the last dynamic start anchor
4-
$lds?: Anchor
5-
})
6-
| undefined
7-
export let insertionAnchor: Node | 0 | undefined | null
1+
export let insertionParent: ParentNode | undefined
2+
export let insertionAnchor: Node | 0 | undefined
83

94
/**
105
* This function is called before a block type that requires insertion
@@ -19,13 +14,3 @@ export function setInsertionState(parent: ParentNode, anchor?: Node | 0): void {
1914
export function resetInsertionState(): void {
2015
insertionParent = insertionAnchor = undefined
2116
}
22-
23-
export function setInsertionAnchor(anchor: Node | null): void {
24-
insertionAnchor = anchor
25-
}
26-
27-
export type Anchor = Comment & {
28-
// cached matching fragment start to avoid repeated traversal
29-
// on nested fragments
30-
$fs?: Anchor
31-
}

0 commit comments

Comments
 (0)