From: daiwei Date: Tue, 18 Mar 2025 09:17:53 +0000 (+0800) Subject: wip: handle template ref forwarding X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=367924ca9e4d8b14f3ac08a9db2e016944709e5f;p=thirdparty%2Fvuejs%2Fcore.git wip: handle template ref forwarding --- diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index e21b179a21..62677d732b 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -563,6 +563,7 @@ export { initFeatureFlags } from './featureFlags' export { createAsyncComponentContext, useAsyncComponentState, + isAsyncWrapper, } from './apiAsyncComponent' /** * @internal diff --git a/packages/runtime-vapor/__tests__/apiDefineAsyncComponent.spec.ts b/packages/runtime-vapor/__tests__/apiDefineAsyncComponent.spec.ts index 4c76d8dbc9..b488b33dca 100644 --- a/packages/runtime-vapor/__tests__/apiDefineAsyncComponent.spec.ts +++ b/packages/runtime-vapor/__tests__/apiDefineAsyncComponent.spec.ts @@ -629,7 +629,7 @@ describe('api: defineAsyncComponent', () => { expect(root.innerHTML).toBe('') }) - test.todo('template ref forwarding', async () => { + test('template ref forwarding', async () => { let resolve: (comp: VaporComponent) => void const Foo = defineVaporAsyncComponent( () => diff --git a/packages/runtime-vapor/src/apiDefineAsyncComponent.ts b/packages/runtime-vapor/src/apiDefineAsyncComponent.ts index 3de8e3f2ab..1a97731cb0 100644 --- a/packages/runtime-vapor/src/apiDefineAsyncComponent.ts +++ b/packages/runtime-vapor/src/apiDefineAsyncComponent.ts @@ -38,7 +38,6 @@ export function defineVaporAsyncComponent( return defineVaporComponent({ name: 'VaporAsyncComponentWrapper', - // @ts-expect-error __asyncLoader: load, // __asyncHydrate(el, instance, hydrate) { @@ -97,7 +96,7 @@ export function defineVaporAsyncComponent( resolvedComp = getResolvedComp() let blockFn if (loaded.value && resolvedComp) { - blockFn = () => createInnerComp(resolvedComp!, instance) + blockFn = () => createInnerComp(resolvedComp!, instance, frag) } else if (error.value && errorComponent) { blockFn = () => createComponent(errorComponent, { error: () => error.value }) @@ -115,7 +114,17 @@ export function defineVaporAsyncComponent( function createInnerComp( comp: VaporComponent, parent: VaporComponentInstance, + frag?: DynamicFragment, ): VaporComponentInstance { const { rawProps, rawSlots, isSingleRoot, appContext } = parent - return createComponent(comp, rawProps, rawSlots, isSingleRoot, appContext) + const i = createComponent(comp, rawProps, rawSlots, isSingleRoot, appContext) + // set ref + frag && frag.setRef && frag.setRef(i) + + // TODO custom element + // pass the custom element callback on to the inner comp + // and remove it from the async wrapper + // i.ce = ce + // delete parent.ce + return i } diff --git a/packages/runtime-vapor/src/apiTemplateRef.ts b/packages/runtime-vapor/src/apiTemplateRef.ts index c5a6c5fb2b..948d008535 100644 --- a/packages/runtime-vapor/src/apiTemplateRef.ts +++ b/packages/runtime-vapor/src/apiTemplateRef.ts @@ -7,8 +7,10 @@ import { } from './component' import { ErrorCodes, + type GenericComponentInstance, type SchedulerJob, callWithErrorHandling, + isAsyncWrapper, queuePostFlushCb, warn, } from '@vue/runtime-dom' @@ -20,6 +22,7 @@ import { isString, remove, } from '@vue/shared' +import type { DynamicFragment } from './block' export type NodeRef = string | Ref | ((ref: Element) => void) export type RefEl = Element | VaporComponentInstance @@ -47,9 +50,22 @@ export function setRef( refFor = false, ): NodeRef | undefined { if (!instance || instance.isUnmounted) return + const isVaporComp = isVaporComponent(el) + if (isVaporComp && isAsyncWrapper(el as GenericComponentInstance)) { + if (!(el as VaporComponentInstance).type.__asyncResolved) { + const frag = (el as VaporComponentInstance).block as DynamicFragment + frag.setRef = (el: RefEl) => setRef(instance, el, ref, oldRef, refFor) + return + } else { + el = ((el as VaporComponentInstance).block as DynamicFragment) + .nodes as RefEl + } + } const setupState: any = __DEV__ ? instance.setupState || {} : null - const refValue = isVaporComponent(el) ? getExposed(el) || el : el + const refValue = isVaporComp + ? getExposed(el as VaporComponentInstance) || el + : el const refs = instance.refs === EMPTY_OBJ ? (instance.refs = {}) : instance.refs diff --git a/packages/runtime-vapor/src/block.ts b/packages/runtime-vapor/src/block.ts index b782afd38d..a0bdcd9465 100644 --- a/packages/runtime-vapor/src/block.ts +++ b/packages/runtime-vapor/src/block.ts @@ -8,6 +8,7 @@ import { import { createComment, createTextNode } from './dom/node' import { EffectScope, pauseTracking, resetTracking } from '@vue/reactivity' import { isHydrating } from './dom/hydration' +import type { RefEl } from './apiTemplateRef' export type Block = | Node @@ -23,6 +24,7 @@ export class VaporFragment { anchor?: Node insert?: (parent: ParentNode, anchor: Node | null) => void remove?: (parent?: ParentNode) => void + setRef?: (el: RefEl) => void constructor(nodes: Block) { this.nodes = nodes diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 548babebf8..4dfc6a09b1 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -92,6 +92,8 @@ export interface ObjectVaporComponent name?: string vapor?: boolean + __asyncLoader?: () => Promise + __asyncResolved?: VaporComponent } interface SharedInternalOptions {