From: daiwei Date: Wed, 13 Aug 2025 09:14:49 +0000 (+0800) Subject: chore: minor tweaks X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=cc8423301efec4214ca386706d2d20a550b927a3;p=thirdparty%2Fvuejs%2Fcore.git chore: minor tweaks --- diff --git a/packages/runtime-vapor/src/dom/hydration.ts b/packages/runtime-vapor/src/dom/hydration.ts index 469d1be453..04b19d64d4 100644 --- a/packages/runtime-vapor/src/dom/hydration.ts +++ b/packages/runtime-vapor/src/dom/hydration.ts @@ -16,30 +16,12 @@ import { BLOCK_APPEND_ANCHOR_LABEL, BLOCK_INSERTION_ANCHOR_LABEL, BLOCK_PREPEND_ANCHOR_LABEL, - isVaporAnchor, } from '@vue/shared' const isHydratingStack = [] as boolean[] - export let isHydrating = false export let currentHydrationNode: Node | null = null -export function setCurrentHydrationNode(node: Node | null): void { - currentHydrationNode = node -} - -function findParentSibling(n: Node): Node | null { - if (!n.parentNode) return null - return n.parentNode.nextSibling || findParentSibling(n.parentNode) -} - -export function advanceHydrationNode(node: Node & { $ps?: Node | null }): void { - // if no next sibling, find the next node in the parent chain - const ret = - node.nextSibling || node.$ps || (node.$ps = findParentSibling(node)) - if (ret) setCurrentHydrationNode(ret) -} - let isOptimized = false function performHydration( @@ -93,6 +75,22 @@ type Anchor = Comment & { export const isComment = (node: Node, data: string): node is Anchor => node.nodeType === 8 && (node as Comment).data === data +export function setCurrentHydrationNode(node: Node | null): void { + currentHydrationNode = node +} + +function findParentSibling(n: Node): Node | null { + if (!n.parentNode) return null + return n.parentNode.nextSibling || findParentSibling(n.parentNode) +} + +export function advanceHydrationNode(node: Node & { $ps?: Node | null }): void { + // if no next sibling, find the next node in the parent chain + const ret = + node.nextSibling || node.$ps || (node.$ps = findParentSibling(node)) + if (ret) setCurrentHydrationNode(ret) +} + /** * Locate the first non-fragment-comment node and locate the next node * while handling potential fragments. @@ -197,17 +195,6 @@ export function locateEndAnchor( return null } -export function isNonHydrationNode(node: Node): boolean { - return ( - // empty text node - isEmptyTextNode(node) || - // vdom fragment end anchor (``) - isComment(node, ']') || - // vapor mode specific anchors - isVaporAnchor(node) - ) -} - export function locateVaporFragmentAnchor( node: Node, anchorLabel: string, @@ -219,10 +206,6 @@ export function locateVaporFragmentAnchor( return null } -export function isEmptyTextNode(node: Node): node is Text { - return node.nodeType === 3 && !(node as Text).data.trim() -} - function locateHydrationNodeByAnchor( node: Node, anchorLabel: string, diff --git a/packages/runtime-vapor/src/dom/node.ts b/packages/runtime-vapor/src/dom/node.ts index 6c85d70fec..47a4368646 100644 --- a/packages/runtime-vapor/src/dom/node.ts +++ b/packages/runtime-vapor/src/dom/node.ts @@ -1,8 +1,8 @@ -import { isComment, isNonHydrationNode, locateEndAnchor } from './hydration' +import { isComment, locateEndAnchor } from './hydration' import { BLOCK_INSERTION_ANCHOR_LABEL, BLOCK_PREPEND_ANCHOR_LABEL, - isVaporAnchor, + isInsertionAnchor, } from '@vue/shared' /*! #__NO_SIDE_EFFECTS__ */ @@ -55,45 +55,20 @@ export function _child(node: ParentNode): Node { /** * Hydration-specific version of `child`. - * - * This function skips leading fragment anchors to find the first node relevant - * for hydration matching against the client-side template structure. - * - * Problem: - * Template: `
{{ msg }}
` - * - * Client Compiled Code (Simplified): - * const n2 = t0() // n2 = `
` - * const n1 = _child(n2, 1) // n1 = text node - * // ... slot creation ... - * _renderEffect(() => _setText(n1, _ctx.msg)) - * - * SSR Output: `
slot contentActual Text Node
` - * - * Hydration Mismatch: - * - During hydration, `n2` refers to the SSR `
`. - * - `_child(n2, 1)` would return ``. - * - The client code expects `n1` to be the text node, but gets the comment. - * The subsequent `_setText(n1, ...)` would fail or target the wrong node. - * - * Solution (`__child`): - * - `__child(n2, offset)` is used during hydration. It skips the block children - * to find the "Actual Text Node", correctly matching the client's expectation - * for `n1`. */ /*! #__NO_SIDE_EFFECTS__ */ export function __child(node: ParentNode, offset?: number): Node { let n = node.firstChild! // when offset is -1, it means we need to get the text node of this element - // since server-side rendering doesn't generate whitespace placeholder text nodes, - // if firstChild is null, manually insert a text node and return it + // since SSR doesn't generate whitespace placeholder text nodes, if firstChild + // is null, manually insert a text node as the first child if (offset === -1 && !n) { node.textContent = ' ' return node.firstChild! } - while (n && (isComment(n, '[') || isVaporAnchor(n))) { + while (n && (isComment(n, '[') || isInsertionAnchor(n))) { // skip block node n = skipBlockNodes(n) as ChildNode if (isComment(n, '[')) { @@ -130,43 +105,6 @@ export function _next(node: Node): Node { /** * Hydration-specific version of `next`. - * - * SSR comment anchors (fragments `...`, block nodes `...`) - * disrupt standard `node.nextSibling` traversal during hydration. `_next` might - * return a comment node or an internal node of a fragment instead of skipping - * the entire fragment block. - * - * Example: - * Template: `
Node1Node2
` (where is a block node placeholder) - * - * Client Compiled Code (Simplified): - * const n2 = t0() // n2 = `
Node1Node2
` - * const n1 = _next(_child(n2)) // n1 = _next(Node1) returns `` - * _setInsertionState(n2, n1) // insertion anchor is `` - * const n0 = _createComponent(_ctx.Comp) // inserted before `` - * - * SSR Output: `
Node1Node3 Node4Node2
` - * - * Hydration Mismatch: - * - During hydration, `n2` refers to the SSR `
`. - * - `_child(n2)` returns `Node1`. - * - `_next(Node1)` would return ``. - * - The client logic expects `n1` to be the node *after* `Node1` in its structure - * (the placeholder), but gets the fragment start anchor `` from SSR. - * - Using `` as the insertion anchor for hydrating the component is incorrect. - * - * Solution (`__next`): - * - During hydration, `next.impl` is `__next`. - * - `n1 = __next(Node1)` is called. - * - `__next` recognizes that the immediate sibling `` is a fragment start anchor. - * - It skips the entire fragment block (`Node3 Node4`). - * - It returns the node immediately *after* the fragment's end anchor, which is `Node2`. - * - This correctly identifies the logical "next sibling" anchor (`Node2`) in the SSR structure, - * allowing the component to be hydrated correctly relative to `Node1` and `Node2`. - * - * This function ensures traversal correctly skips over non-hydration nodes and - * treats entire fragment/block nodes (when starting *from* their beginning anchor) - * as single logical units to find the next actual sibling node for hydration matching. */ /*! #__NO_SIDE_EFFECTS__ */ export function __next(node: Node): Node { @@ -174,14 +112,8 @@ export function __next(node: Node): Node { if (isComment(node, '[')) { node = locateEndAnchor(node)! } - node = skipBlockNodes(node) - - let n = node.nextSibling! - while (n && isNonHydrationNode(n)) { - n = n.nextSibling! - } - return n + return node.nextSibling! } type DelegatedFunction any> = T & { diff --git a/packages/shared/src/domAnchors.ts b/packages/shared/src/domAnchors.ts index 25b1b975c9..894551f2d9 100644 --- a/packages/shared/src/domAnchors.ts +++ b/packages/shared/src/domAnchors.ts @@ -8,30 +8,10 @@ export const SLOT_ANCHOR_LABEL: string = 'slot' export function isInsertionAnchor(node: Node): node is Comment { if (node.nodeType !== 8) return false - const data = (node as Comment).data return ( data === `[${BLOCK_INSERTION_ANCHOR_LABEL}` || - data === `${BLOCK_INSERTION_ANCHOR_LABEL}]` || data === `[${BLOCK_APPEND_ANCHOR_LABEL}` || - data === `${BLOCK_APPEND_ANCHOR_LABEL}]` || - data === `[${BLOCK_PREPEND_ANCHOR_LABEL}` || - data === `${BLOCK_PREPEND_ANCHOR_LABEL}]` - ) -} - -export function isVaporFragmentAnchor(node: Node): node is Comment { - if (node.nodeType !== 8) return false - - const data = (node as Comment).data - return ( - data === IF_ANCHOR_LABEL || - data === FOR_ANCHOR_LABEL || - data === SLOT_ANCHOR_LABEL || - data === DYNAMIC_COMPONENT_ANCHOR_LABEL + data === `[${BLOCK_PREPEND_ANCHOR_LABEL}` ) } - -export function isVaporAnchor(node: Node): node is Comment { - return isVaporFragmentAnchor(node) || isInsertionAnchor(node) -}