mirror of https://github.com/vuejs/core.git
refactor: add enableHydrationNodeLookup and disableHydrationNodeLookup functions for node handling
This commit is contained in:
parent
1248172216
commit
25b8fbe2fd
|
@ -84,6 +84,10 @@ const getContainerType = (
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isDynamicAnchor(node: Node): boolean {
|
||||||
|
return isComment(node) && (node.data === '[[' || node.data === ']]')
|
||||||
|
}
|
||||||
|
|
||||||
export const isComment = (node: Node): node is Comment =>
|
export const isComment = (node: Node): node is Comment =>
|
||||||
node.nodeType === DOMNodeTypes.COMMENT
|
node.nodeType === DOMNodeTypes.COMMENT
|
||||||
|
|
||||||
|
@ -119,10 +123,6 @@ export function createHydrationFunctions(
|
||||||
},
|
},
|
||||||
} = rendererInternals
|
} = rendererInternals
|
||||||
|
|
||||||
function isDynamicAnchor(node: Node): boolean {
|
|
||||||
return isComment(node) && (node.data === '[[' || node.data === ']]')
|
|
||||||
}
|
|
||||||
|
|
||||||
function nextSibling(node: Node) {
|
function nextSibling(node: Node) {
|
||||||
let n = next(node)
|
let n = next(node)
|
||||||
// skip dynamic child anchor
|
// skip dynamic child anchor
|
||||||
|
|
|
@ -557,3 +557,7 @@ export { startMeasure, endMeasure } from './profiling'
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export { initFeatureFlags } from './featureFlags'
|
export { initFeatureFlags } from './featureFlags'
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export { isDynamicAnchor } from './hydration'
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
import { warn } from '@vue/runtime-dom'
|
import { warn } from '@vue/runtime-dom'
|
||||||
import {
|
import {
|
||||||
type Anchor,
|
|
||||||
insertionAnchor,
|
insertionAnchor,
|
||||||
insertionParent,
|
insertionParent,
|
||||||
resetInsertionState,
|
resetInsertionState,
|
||||||
setInsertionState,
|
setInsertionState,
|
||||||
} from '../insertionState'
|
} from '../insertionState'
|
||||||
import { child, next } from './node'
|
import {
|
||||||
|
child,
|
||||||
|
disableHydrationNodeLookup,
|
||||||
|
enableHydrationNodeLookup,
|
||||||
|
next,
|
||||||
|
} from './node'
|
||||||
|
|
||||||
export let isHydrating = false
|
export let isHydrating = false
|
||||||
export let currentHydrationNode: Node | null = null
|
export let currentHydrationNode: Node | null = null
|
||||||
|
@ -25,18 +29,26 @@ export function withHydration(container: ParentNode, fn: () => void): void {
|
||||||
;(Comment.prototype as any).$fs = undefined
|
;(Comment.prototype as any).$fs = undefined
|
||||||
isOptimized = true
|
isOptimized = true
|
||||||
}
|
}
|
||||||
|
enableHydrationNodeLookup()
|
||||||
isHydrating = true
|
isHydrating = true
|
||||||
setInsertionState(container, 0)
|
setInsertionState(container, 0)
|
||||||
const res = fn()
|
const res = fn()
|
||||||
resetInsertionState()
|
resetInsertionState()
|
||||||
currentHydrationNode = null
|
currentHydrationNode = null
|
||||||
isHydrating = false
|
isHydrating = false
|
||||||
|
disableHydrationNodeLookup()
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
export let adoptTemplate: (node: Node, template: string) => Node | null
|
export let adoptTemplate: (node: Node, template: string) => Node | null
|
||||||
export let locateHydrationNode: () => void
|
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 =>
|
export const isComment = (node: Node, data: string): node is Anchor =>
|
||||||
node.nodeType === 8 && (node as Comment).data === data
|
node.nodeType === 8 && (node as Comment).data === data
|
||||||
|
|
||||||
|
@ -120,10 +132,6 @@ function locateHydrationNodeImpl() {
|
||||||
currentHydrationNode = node
|
currentHydrationNode = node
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isDynamicAnchor(node: Node): node is Comment {
|
|
||||||
return isComment(node, '[[') || isComment(node, ']]')
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isEmptyText(node: Node): node is Text {
|
export function isEmptyText(node: Node): node is Text {
|
||||||
return node.nodeType === 3 && !(node as Text).data.trim()
|
return node.nodeType === 3 && !(node as Text).data.trim()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
import {
|
import { isDynamicAnchor } from '@vue/runtime-dom'
|
||||||
isComment,
|
import { isComment, isEmptyText, locateEndAnchor } from './hydration'
|
||||||
isDynamicAnchor,
|
|
||||||
isEmptyText,
|
|
||||||
isHydrating,
|
|
||||||
locateEndAnchor,
|
|
||||||
} from './hydration'
|
|
||||||
|
|
||||||
/*! #__NO_SIDE_EFFECTS__ */
|
/*! #__NO_SIDE_EFFECTS__ */
|
||||||
export function createTextNode(value = ''): Text {
|
export function createTextNode(value = ''): Text {
|
||||||
|
@ -27,9 +22,12 @@ export function child(node: ParentNode): Node {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! #__NO_SIDE_EFFECTS__ */
|
/*! #__NO_SIDE_EFFECTS__ */
|
||||||
export function nthChild(node: Node, i: number): Node {
|
export function _nthChild(node: Node, i: number): Node {
|
||||||
if (!isHydrating) return node.childNodes[i]
|
return node.childNodes[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! #__NO_SIDE_EFFECTS__ */
|
||||||
|
export function __nthChild(node: Node, i: number): Node {
|
||||||
let n = node.firstChild!
|
let n = node.firstChild!
|
||||||
for (let start = 0; start < i; start++) {
|
for (let start = 0; start < i; start++) {
|
||||||
n = next(n) as ChildNode
|
n = next(n) as ChildNode
|
||||||
|
@ -38,9 +36,12 @@ export function nthChild(node: Node, i: number): Node {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! #__NO_SIDE_EFFECTS__ */
|
/*! #__NO_SIDE_EFFECTS__ */
|
||||||
export function next(node: Node): Node {
|
function _next(node: Node): Node {
|
||||||
if (!isHydrating) return node.nextSibling!
|
return node.nextSibling!
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! #__NO_SIDE_EFFECTS__ */
|
||||||
|
function __next(node: Node): Node {
|
||||||
// process fragment as a single node
|
// process fragment as a single node
|
||||||
if (node && isComment(node, '[')) {
|
if (node && isComment(node, '[')) {
|
||||||
node = locateEndAnchor(node)!
|
node = locateEndAnchor(node)!
|
||||||
|
@ -53,3 +54,46 @@ export function next(node: Node): Node {
|
||||||
}
|
}
|
||||||
return n
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
export let insertionParent:
|
export let insertionParent: ParentNode | undefined
|
||||||
| (ParentNode & {
|
export let insertionAnchor: Node | 0 | undefined
|
||||||
// cached the last dynamic start anchor
|
|
||||||
$lds?: Anchor
|
|
||||||
})
|
|
||||||
| undefined
|
|
||||||
export let insertionAnchor: Node | 0 | undefined | null
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function is called before a block type that requires insertion
|
* 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 {
|
export function resetInsertionState(): void {
|
||||||
insertionParent = insertionAnchor = undefined
|
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
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue