return { container }
}
- function asyncWrapper(code: string) {
+ function withAsyncScript(code: string) {
return {
code: `
<script vapor>
- const data = _data;
+ const data = _data;
+ const components = _components;
const p = new Promise(r => setTimeout(r, 5))
data.deps.push(p.then(() => Promise.resolve()))
await p
<Suspense>
<components.VaporChild/>
<template #fallback>
- <span>loading</span>
+ <span>fallback</span>
</template>
</Suspense>
</template>`,
{
- VaporChild: asyncWrapper(`<template><div>hi</div></template>`),
+ VaporChild: withAsyncScript(`<template><div>hi</div></template>`),
},
data,
)
- expect(container.innerHTML).toBe(`<span>loading</span>`)
+ expect(container.innerHTML).toBe(`<span>fallback</span>`)
expect(data.deps.length).toBe(1)
await Promise.all(data.deps)
await nextTick()
expect(container.innerHTML).toBe(`<div>hi</div>`)
})
+
+ test('vdom suspense: nested async vapor components', async () => {
+ const data = { deps: [] }
+ const { container } = await testSuspense(
+ `<script setup>
+ const components = _components;
+ </script>
+ <template>
+ <Suspense>
+ <components.AsyncOuter/>
+ <template #fallback>
+ <span>fallback</span>
+ </template>
+ </Suspense>
+ </template>`,
+ {
+ AsyncOuter: withAsyncScript(
+ `<template><components.AsyncInner/></template>`,
+ ),
+ AsyncInner: withAsyncScript(`<template><div>inner</div></template>`),
+ },
+ data,
+ )
+
+ expect(container.innerHTML).toBe(`<span>fallback</span>`)
+
+ await data.deps[0]
+ await nextTick()
+ expect(container.innerHTML).toBe(`<span>fallback</span>`)
+
+ await Promise.all(data.deps)
+ await nextTick()
+ expect(container.innerHTML).toBe(`<div>inner</div>`)
+ })
})
import { hmrReload, hmrRerender } from './hmr'
import { isHydrating, locateHydrationNode } from './dom/hydration'
import { insertionAnchor, insertionParent } from './insertionState'
+import { parentSuspense } from './components/Suspense'
export { currentInstance } from '@vue/runtime-dom'
appContext: GenericAppContext = (currentInstance &&
currentInstance.appContext) ||
emptyContext,
- parentSuspense?: SuspenseBoundary | null,
): VaporComponentInstance {
const _insertionParent = insertionParent
const _insertionAnchor = insertionAnchor
parent: ParentNode,
anchor?: Node | null | 0,
): void {
+ if (
+ __FEATURE_SUSPENSE__ &&
+ instance.suspense &&
+ instance.asyncDep &&
+ !instance.asyncResolved
+ ) {
+ const component = instance.type as any
+ instance.suspense.registerDep(
+ instance as any,
+ (setupResult: any) => {
+ handleSetupResult(
+ setupResult,
+ component,
+ instance,
+ undefined,
+ isFunction(component) ? component : component.setup,
+ )
+ mountComponent(instance, parent, anchor)
+ },
+ false,
+ )
+ return
+ }
+
if (__DEV__) {
startMeasure(instance, `mount`)
}
--- /dev/null
+import type { SuspenseBoundary } from '@vue/runtime-dom'
+
+export let parentSuspense: SuspenseBoundary | null = null
+export function setParentSuspense(suspense: SuspenseBoundary | null): void {
+ parentSuspense = suspense
+}
+
+// TODO
+export const VaporSuspenseImpl = {
+ name: 'VaporSuspense',
+ __isSuspense: true,
+ process(): void {},
+}
type VaporComponent,
VaporComponentInstance,
createComponent,
- handleSetupResult,
mountComponent,
unmountComponent,
} from './component'
import { renderEffect } from './renderEffect'
import { createTextNode } from './dom/node'
import { optimizePropertyLookup } from './dom/prop'
+import { setParentSuspense } from './components/Suspense'
// mounting vapor components and slots in vdom
const vaporInteropImpl: Omit<
const propsRef = shallowRef(vnode.props)
const slotsRef = shallowRef(vnode.children)
+ if (__FEATURE_SUSPENSE__) {
+ setParentSuspense(parentSuspense)
+ }
+
const component = vnode.type as any as VaporComponent
// @ts-expect-error
const instance = (vnode.component = createComponent(
} as any as RawSlots,
undefined,
undefined,
- parentSuspense,
))
instance.rawPropsRef = propsRef
instance.rawSlotsRef = slotsRef
- if (__FEATURE_SUSPENSE__ && parentSuspense && instance.asyncDep) {
- parentSuspense.registerDep(
- instance as any,
- (setupResult: any) => {
- handleSetupResult(
- setupResult,
- component,
- instance,
- undefined,
- isFunction(component) ? component : component.setup,
- )
- mountComponent(instance, container, selfAnchor)
- },
- false,
- )
- } else {
- mountComponent(instance, container, selfAnchor)
- }
+ mountComponent(instance, container, selfAnchor)
simpleSetCurrentInstance(prev)
return instance
},