From: daiwei Date: Fri, 26 Dec 2025 01:38:51 +0000 (+0800) Subject: refactor(runtime-vapor): extract DynamicFragment hydration logic for treeshaking X-Git-Tag: v3.6.0-beta.2~11 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3df648f71306aed9d206a2f24003a1d9d7eec291;p=thirdparty%2Fvuejs%2Fcore.git refactor(runtime-vapor): extract DynamicFragment hydration logic for treeshaking --- diff --git a/packages/runtime-vapor/src/dom/hydration.ts b/packages/runtime-vapor/src/dom/hydration.ts index 5ad6f1e307..8c1ae81af9 100644 --- a/packages/runtime-vapor/src/dom/hydration.ts +++ b/packages/runtime-vapor/src/dom/hydration.ts @@ -1,4 +1,9 @@ -import { MismatchTypes, isMismatchAllowed, warn } from '@vue/runtime-dom' +import { + MismatchTypes, + isMismatchAllowed, + queuePostFlushCb, + warn, +} from '@vue/runtime-dom' import { type ChildItem, insertionAnchor, @@ -9,6 +14,7 @@ import { import { _child, _next, + createComment, createElement, createTextNode, disableHydrationNodeLookup, @@ -16,12 +22,14 @@ import { locateChildByLogicalIndex, parentNode, } from './node' -import { remove } from '../block' +import { findBlockNode, remove } from '../block' const isHydratingStack = [] as boolean[] export let isHydrating = false export let currentHydrationNode: Node | null = null +let _hydrateDynamicFragment: ((frag: any, isEmpty: boolean) => void) | undefined + function pushIsHydrating(value: boolean): void { isHydratingStack.push((isHydrating = value)) } @@ -50,6 +58,7 @@ function performHydration( if (!isOptimized) { adoptTemplate = adoptTemplateImpl locateHydrationNode = locateHydrationNodeImpl + _hydrateDynamicFragment = hydrateDynamicFragmentImpl // optimize anchor cache lookup ;(Comment.prototype as any).$fe = undefined ;(Node.prototype as any).$pns = undefined @@ -307,3 +316,62 @@ export function removeFragmentNodes(node: Node, endAnchor?: Node): void { } } } + +export function hydrateDynamicFragment(frag: any, isEmpty: boolean): void { + _hydrateDynamicFragment && _hydrateDynamicFragment(frag, isEmpty) +} + +// Hydrate implementation for DynamicFragment +function hydrateDynamicFragmentImpl(frag: any, isEmpty: boolean): void { + // avoid repeated hydration during fallback rendering + if (frag.anchor) return + + if (frag.anchorLabel === 'if') { + // reuse the empty comment node as the anchor for empty if + // e.g. `
` -> `` + if (isEmpty) { + frag.anchor = locateFragmentEndAnchor('')! + if (__DEV__ && !frag.anchor) { + throw new Error( + 'Failed to locate if anchor. this is likely a Vue internal bug.', + ) + } else { + if (__DEV__) { + ;(frag.anchor as Comment).data = frag.anchorLabel + } + return + } + } + } else if (frag.anchorLabel === 'slot') { + // reuse the empty comment node for empty slot + // e.g. `` + if (isEmpty && isComment(currentHydrationNode!, '')) { + frag.anchor = currentHydrationNode! + if (__DEV__) { + ;(frag.anchor as Comment).data = frag.anchorLabel! + } + return + } + + // reuse the vdom fragment end anchor + frag.anchor = locateFragmentEndAnchor()! + if (__DEV__ && !frag.anchor) { + throw new Error( + 'Failed to locate slot anchor. this is likely a Vue internal bug.', + ) + } else { + return + } + } + + const { parentNode: pn, nextNode } = findBlockNode(frag.nodes)! + // create an anchor + queuePostFlushCb(() => { + pn!.insertBefore( + (frag.anchor = __DEV__ + ? createComment(frag.anchorLabel!) + : createTextNode()), + nextNode, + ) + }) +} diff --git a/packages/runtime-vapor/src/fragment.ts b/packages/runtime-vapor/src/fragment.ts index 28df9a27be..9f0368734a 100644 --- a/packages/runtime-vapor/src/fragment.ts +++ b/packages/runtime-vapor/src/fragment.ts @@ -7,7 +7,6 @@ import { type VaporTransitionHooks, applyTransitionHooks, applyTransitionLeaveHooks, - findBlockNode, insert, isValidBlock, remove, @@ -17,17 +16,14 @@ import { type TransitionHooks, type VNode, currentInstance, - queuePostFlushCb, setCurrentInstance, warnExtraneousAttributes, } from '@vue/runtime-dom' import { type VaporComponentInstance, applyFallthroughProps } from './component' import type { NodeRef } from './apiTemplateRef' import { - currentHydrationNode, - isComment, + hydrateDynamicFragment, isHydrating, - locateFragmentEndAnchor, locateHydrationNode, } from './dom/hydration' import { isArray } from '@vue/shared' @@ -278,57 +274,7 @@ export class DynamicFragment extends VaporFragment { } hydrate = (isEmpty = false): void => { - // avoid repeated hydration during fallback rendering - if (this.anchor) return - - if (this.anchorLabel === 'if') { - // reuse the empty comment node as the anchor for empty if - // e.g. `
` -> `` - if (isEmpty) { - this.anchor = locateFragmentEndAnchor('')! - if (__DEV__ && !this.anchor) { - throw new Error( - 'Failed to locate if anchor. this is likely a Vue internal bug.', - ) - } else { - if (__DEV__) { - ;(this.anchor as Comment).data = this.anchorLabel - } - return - } - } - } else if (this.anchorLabel === 'slot') { - // reuse the empty comment node for empty slot - // e.g. `` - if (isEmpty && isComment(currentHydrationNode!, '')) { - this.anchor = currentHydrationNode! - if (__DEV__) { - ;(this.anchor as Comment).data = this.anchorLabel! - } - return - } - - // reuse the vdom fragment end anchor - this.anchor = locateFragmentEndAnchor()! - if (__DEV__ && !this.anchor) { - throw new Error( - 'Failed to locate slot anchor. this is likely a Vue internal bug.', - ) - } else { - return - } - } - - const { parentNode, nextNode } = findBlockNode(this.nodes)! - // create an anchor - queuePostFlushCb(() => { - parentNode!.insertBefore( - (this.anchor = __DEV__ - ? createComment(this.anchorLabel!) - : createTextNode()), - nextNode, - ) - }) + hydrateDynamicFragment(this, isEmpty) } }