export {
createAsyncComponentContext,
useAsyncComponentState,
+ isAsyncWrapper,
} from './apiAsyncComponent'
/**
* @internal
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(
() =>
return defineVaporComponent({
name: 'VaporAsyncComponentWrapper',
- // @ts-expect-error
__asyncLoader: load,
// __asyncHydrate(el, instance, hydrate) {
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 })
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
}
} from './component'
import {
ErrorCodes,
+ type GenericComponentInstance,
type SchedulerJob,
callWithErrorHandling,
+ isAsyncWrapper,
queuePostFlushCb,
warn,
} from '@vue/runtime-dom'
isString,
remove,
} from '@vue/shared'
+import type { DynamicFragment } from './block'
export type NodeRef = string | Ref | ((ref: Element) => void)
export type RefEl = Element | VaporComponentInstance
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
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
anchor?: Node
insert?: (parent: ParentNode, anchor: Node | null) => void
remove?: (parent?: ParentNode) => void
+ setRef?: (el: RefEl) => void
constructor(nodes: Block) {
this.nodes = nodes
name?: string
vapor?: boolean
+ __asyncLoader?: () => Promise<VaporComponent>
+ __asyncResolved?: VaporComponent
}
interface SharedInternalOptions {