]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: handle template ref forwarding
authordaiwei <daiwei521@126.com>
Tue, 18 Mar 2025 09:17:53 +0000 (17:17 +0800)
committerdaiwei <daiwei521@126.com>
Tue, 18 Mar 2025 09:17:53 +0000 (17:17 +0800)
packages/runtime-core/src/index.ts
packages/runtime-vapor/__tests__/apiDefineAsyncComponent.spec.ts
packages/runtime-vapor/src/apiDefineAsyncComponent.ts
packages/runtime-vapor/src/apiTemplateRef.ts
packages/runtime-vapor/src/block.ts
packages/runtime-vapor/src/component.ts

index e21b179a219ca50615609b69b1c8aa0e208c0e5f..62677d732b8cde52062357d6d30d54a93eb8f026 100644 (file)
@@ -563,6 +563,7 @@ export { initFeatureFlags } from './featureFlags'
 export {
   createAsyncComponentContext,
   useAsyncComponentState,
+  isAsyncWrapper,
 } from './apiAsyncComponent'
 /**
  * @internal
index 4c76d8dbc99f86be1a50663bcae71f04f3005ce8..b488b33dca7f601cee8ce23ca11313e6d30a342a 100644 (file)
@@ -629,7 +629,7 @@ describe('api: defineAsyncComponent', () => {
     expect(root.innerHTML).toBe('<!--async component-->')
   })
 
-  test.todo('template ref forwarding', async () => {
+  test('template ref forwarding', async () => {
     let resolve: (comp: VaporComponent) => void
     const Foo = defineVaporAsyncComponent(
       () =>
index 3de8e3f2ab33003d1ddd24fbe7ed861ae73750eb..1a97731cb0ec424a238b930e32d88cda7f487c77 100644 (file)
@@ -38,7 +38,6 @@ export function defineVaporAsyncComponent<T extends VaporComponent>(
   return defineVaporComponent({
     name: 'VaporAsyncComponentWrapper',
 
-    // @ts-expect-error
     __asyncLoader: load,
 
     // __asyncHydrate(el, instance, hydrate) {
@@ -97,7 +96,7 @@ export function defineVaporAsyncComponent<T extends VaporComponent>(
         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<T extends VaporComponent>(
 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
 }
index c5a6c5fb2b67b8b50e855686f3f6dd61c8a60338..948d008535b21bf8b4ac90ffd724cf72a9e3540f 100644 (file)
@@ -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
index b782afd38d35b66c9fed33675d4f4705efbceb3f..a0bdcd9465966fca99f9fe0100f0950ff0bb6b54 100644 (file)
@@ -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
index 548babebf8beef2115e31356d50a989e2e1a0112..4dfc6a09b1df69ac856af7d5d0d098ceae92873f 100644 (file)
@@ -92,6 +92,8 @@ export interface ObjectVaporComponent
 
   name?: string
   vapor?: boolean
+  __asyncLoader?: () => Promise<VaporComponent>
+  __asyncResolved?: VaporComponent
 }
 
 interface SharedInternalOptions {