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
;(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
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()
}
-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 {
}
/*! #__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
}
/*! #__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)!
}
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
+}
-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
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
-}