From 3c72d202ff84f5e1bdaa5658ea6d4dfd53fcbf59 Mon Sep 17 00:00:00 2001 From: edison Date: Thu, 15 Jan 2026 11:42:25 +0800 Subject: [PATCH] refactor(runtime-vapor): inline async hydration logic (#14320) --- .../src/apiDefineAsyncComponent.ts | 180 +++++++----------- packages/runtime-vapor/src/dom/hydration.ts | 2 - 2 files changed, 74 insertions(+), 108 deletions(-) diff --git a/packages/runtime-vapor/src/apiDefineAsyncComponent.ts b/packages/runtime-vapor/src/apiDefineAsyncComponent.ts index 91e04c7373..b5408f747f 100644 --- a/packages/runtime-vapor/src/apiDefineAsyncComponent.ts +++ b/packages/runtime-vapor/src/apiDefineAsyncComponent.ts @@ -30,103 +30,6 @@ import { type TransitionOptions, insert, remove } from './block' import { _next, parentNode } from './dom/node' import { invokeArrayFns } from '@vue/shared' -export type AsyncHydrateImplFn = ( - el: Element, - instance: VaporComponentInstance, - hydrate: () => void, - load: () => Promise, - getResolvedComp: () => VaporComponent | undefined, - hydrateStrategy: any, -) => void - -let asyncHydrate: AsyncHydrateImplFn | undefined - -// Register async hydrate implementation for tree-shaking -export function registerAsyncHydrateImpl(): void { - asyncHydrate = asyncHydrateImpl -} - -const asyncHydrateImpl: AsyncHydrateImplFn = ( - el: Element, - instance: VaporComponentInstance, - hydrate: () => void, - load: () => Promise, - getResolvedComp: () => VaporComponent | undefined, - hydrateStrategy: any, -) => { - // Create placeholder block that matches the adopted DOM. - // The async component may get unmounted before its inner component is loaded, - // so we need to give it a placeholder block. - if (isComment(el, '[')) { - const end = _next(locateEndAnchor(el)!) - const block = (instance.block = [el as Node]) - let cur = el as Node - while (true) { - let n = _next(cur) - if (n && n !== end) { - block.push((cur = n)) - } else { - break - } - } - } else { - instance.block = el - } - - // Mark as mounted to ensure it can be unmounted before - // its inner component is resolved - instance.isMounted = true - - // Advance current hydration node to the nextSibling - setCurrentHydrationNode( - isComment(el, '[') ? locateEndAnchor(el)! : el.nextSibling, - ) - - // If async component needs to be updated before hydration, hydration is no longer needed. - let isHydrated = false - watch( - () => instance.attrs, - () => { - // early return if already hydrated - if (isHydrated) return - - // call the beforeUpdate hook to avoid calling hydrate in performAsyncHydrate - instance.bu && invokeArrayFns(instance.bu) - - // mount the inner component and remove the placeholder - const parent = parentNode(el)! - load().then(() => { - if (instance.isUnmounted) return - hydrate() - if (isComment(el, '[')) { - const endAnchor = locateEndAnchor(el)! - removeFragmentNodes(el, endAnchor) - insert(instance.block, parent, endAnchor) - } else { - insert(instance.block, parent, el) - remove(el, parent) - } - }) - }, - { deep: true, once: true }, - ) - - performAsyncHydrate( - el, - instance, - () => { - hydrateNode(el, () => { - hydrate() - insert(instance.block, parentNode(el)!, el) - isHydrated = true - }) - }, - getResolvedComp, - load, - hydrateStrategy, - ) -} - /*@ __NO_SIDE_EFFECTS__ */ export function defineVaporAsyncComponent( source: AsyncComponentLoader | AsyncComponentOptions, @@ -157,15 +60,80 @@ export function defineVaporAsyncComponent( // not the actual hydrate function hydrate: () => void, ) { - asyncHydrate && - asyncHydrate( - el, - instance, - hydrate, - load, - getResolvedComp, - hydrateStrategy, - ) + // early return allows tree-shaking of hydration logic when not used + if (!isHydrating) return + + // Create placeholder block that matches the adopted DOM. + // The async component may get unmounted before its inner component is loaded, + // so we need to give it a placeholder block. + if (isComment(el, '[')) { + const end = _next(locateEndAnchor(el)!) + const block = (instance.block = [el as Node]) + let cur = el as Node + while (true) { + let n = _next(cur) + if (n && n !== end) { + block.push((cur = n)) + } else { + break + } + } + } else { + instance.block = el + } + + // Mark as mounted to ensure it can be unmounted before + // its inner component is resolved + instance.isMounted = true + + // Advance current hydration node to the nextSibling + setCurrentHydrationNode( + isComment(el, '[') ? locateEndAnchor(el)! : el.nextSibling, + ) + + // If async component needs to be updated before hydration, hydration is no longer needed. + let isHydrated = false + watch( + () => instance.attrs, + () => { + // early return if already hydrated + if (isHydrated) return + + // call the beforeUpdate hook to avoid calling hydrate in performAsyncHydrate + instance.bu && invokeArrayFns(instance.bu) + + // mount the inner component and remove the placeholder + const parent = parentNode(el)! + load().then(() => { + if (instance.isUnmounted) return + hydrate() + if (isComment(el, '[')) { + const endAnchor = locateEndAnchor(el)! + removeFragmentNodes(el, endAnchor) + insert(instance.block, parent, endAnchor) + } else { + insert(instance.block, parent, el) + remove(el, parent) + } + }) + }, + { deep: true, once: true }, + ) + + performAsyncHydrate( + el, + instance, + () => { + hydrateNode(el, () => { + hydrate() + insert(instance.block, parentNode(el)!, el) + isHydrated = true + }) + }, + getResolvedComp, + load, + hydrateStrategy, + ) }, get __asyncResolved() { diff --git a/packages/runtime-vapor/src/dom/hydration.ts b/packages/runtime-vapor/src/dom/hydration.ts index 63fc4f1519..6214904e3e 100644 --- a/packages/runtime-vapor/src/dom/hydration.ts +++ b/packages/runtime-vapor/src/dom/hydration.ts @@ -24,7 +24,6 @@ import { } from './node' import { findBlockNode, remove } from '../block' import type { DynamicFragment } from '../fragment' -import { registerAsyncHydrateImpl } from '../apiDefineAsyncComponent' export let currentHydrationNode: Node | null = null @@ -61,7 +60,6 @@ function performHydration( adoptTemplate = adoptTemplateImpl locateHydrationNode = locateHydrationNodeImpl _hydrateDynamicFragment = hydrateDynamicFragmentImpl - registerAsyncHydrateImpl() // optimize anchor cache lookup ;(Comment.prototype as any).$fe = undefined ;(Node.prototype as any).$pns = undefined -- 2.47.3