]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: save
authordaiwei <daiwei521@126.com>
Tue, 20 May 2025 08:51:39 +0000 (16:51 +0800)
committerdaiwei <daiwei521@126.com>
Tue, 20 May 2025 08:55:06 +0000 (16:55 +0800)
packages/runtime-vapor/__tests__/components/Suspense.spec.ts
packages/runtime-vapor/src/component.ts
packages/runtime-vapor/src/components/Suspense.ts [new file with mode: 0644]
packages/runtime-vapor/src/vdomInterop.ts

index 6bec5b8a3854ceeb89457e8db7c883a9adf53832..36cafc55f35d117e6b2158b32664c3513f81f2d9 100644 (file)
@@ -35,11 +35,12 @@ describe('vapor / vdom interop', () => {
     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
@@ -60,20 +61,54 @@ describe('vapor / vdom interop', () => {
         <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>`)
+  })
 })
index f82ae268291e236478ecb1cabedd3b0be71a2d82..b169f01260936bd454d958c09e8c4f91eb44efd3 100644 (file)
@@ -67,6 +67,7 @@ import {
 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'
 
@@ -144,7 +145,6 @@ export function createComponent(
   appContext: GenericAppContext = (currentInstance &&
     currentInstance.appContext) ||
     emptyContext,
-  parentSuspense?: SuspenseBoundary | null,
 ): VaporComponentInstance {
   const _insertionParent = insertionParent
   const _insertionAnchor = insertionAnchor
@@ -483,6 +483,30 @@ export function mountComponent(
   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`)
   }
diff --git a/packages/runtime-vapor/src/components/Suspense.ts b/packages/runtime-vapor/src/components/Suspense.ts
new file mode 100644 (file)
index 0000000..5dafe58
--- /dev/null
@@ -0,0 +1,13 @@
+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 {},
+}
index 8269b201722ee6f287a347134fee88c647a3f24a..fed9666e0a04534950e8eff201bcb3e74b17abe0 100644 (file)
@@ -23,7 +23,6 @@ import {
   type VaporComponent,
   VaporComponentInstance,
   createComponent,
-  handleSetupResult,
   mountComponent,
   unmountComponent,
 } from './component'
@@ -34,6 +33,7 @@ import type { RawSlots, VaporSlot } from './componentSlots'
 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<
@@ -49,6 +49,10 @@ 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(
@@ -61,28 +65,10 @@ const vaporInteropImpl: Omit<
       } 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
   },