Skip to content

Commit 0cf3a63

Browse files
committed
refactor: add preHydration and postHydration functions for node handling
1 parent 1248172 commit 0cf3a63

File tree

4 files changed

+66
-20
lines changed

4 files changed

+66
-20
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

+3-5
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
resetInsertionState,
77
setInsertionState,
88
} from '../insertionState'
9-
import { child, next } from './node'
9+
import { child, next, postHydration, preHydration } from './node'
1010

1111
export let isHydrating = false
1212
export let currentHydrationNode: Node | null = null
@@ -25,12 +25,14 @@ export function withHydration(container: ParentNode, fn: () => void): void {
2525
;(Comment.prototype as any).$fs = undefined
2626
isOptimized = true
2727
}
28+
preHydration()
2829
isHydrating = true
2930
setInsertionState(container, 0)
3031
const res = fn()
3132
resetInsertionState()
3233
currentHydrationNode = null
3334
isHydrating = false
35+
postHydration()
3436
return res
3537
}
3638

@@ -120,10 +122,6 @@ function locateHydrationNodeImpl() {
120122
currentHydrationNode = node
121123
}
122124

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

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 preHydration(): void {
92+
next.impl = __next
93+
nthChild.impl = __nthChild
94+
}
95+
96+
export function postHydration(): void {
97+
next.impl = _next
98+
nthChild.impl = _nthChild
99+
}

0 commit comments

Comments
 (0)