]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(useId): properly mark async boundary for already resolved async component
authorEvan You <evan@vuejs.org>
Wed, 24 Jul 2024 15:08:56 +0000 (23:08 +0800)
committerEvan You <evan@vuejs.org>
Wed, 24 Jul 2024 15:08:56 +0000 (23:08 +0800)
packages/runtime-core/__tests__/helpers/useId.spec.ts
packages/runtime-core/src/apiAsyncComponent.ts
packages/runtime-core/src/component.ts

index 17fdd24d01b9876c5444587a0d3212714ee42b5d..564755e2fd84697c72831e179d0753567a6db182 100644 (file)
@@ -12,10 +12,11 @@ import {
 } from 'vue'
 import { renderToString } from '@vue/server-renderer'
 
-type TestCaseFactory = () => [App, Promise<any>[]]
+type FactoryRes = [App, Promise<any>[]]
+type TestCaseFactory = () => FactoryRes | Promise<FactoryRes>
 
 async function runOnClient(factory: TestCaseFactory) {
-  const [app, deps] = factory()
+  const [app, deps] = await factory()
   const root = document.createElement('div')
   app.mount(root)
   await Promise.all(deps)
@@ -24,7 +25,7 @@ async function runOnClient(factory: TestCaseFactory) {
 }
 
 async function runOnServer(factory: TestCaseFactory) {
-  const [app, _] = factory()
+  const [app, _] = await factory()
   return (await renderToString(app))
     .replace(/<!--[\[\]]-->/g, '') // remove fragment wrappers
     .trim()
@@ -240,11 +241,11 @@ describe('useId', () => {
     expect(await getOutput(() => factory())).toBe(expected)
   })
 
-  test('async component inside async setup', async () => {
-    const factory = (
+  test('async component inside async setup, already resolved', async () => {
+    const factory = async (
       delay1: number,
       delay2: number,
-    ): ReturnType<TestCaseFactory> => {
+    ): Promise<FactoryRes> => {
       const p1 = promiseWithDelay(null, delay1)
       const p2 = promiseWithDelay(BasicComponentWithUseId, delay2)
       const AsyncInner = defineAsyncComponent(() => p2)
@@ -269,6 +270,9 @@ describe('useId', () => {
             })
         },
       })
+
+      // the async component may have already been resolved
+      await AsyncInner.__asyncLoader()
       return [app, [p1, p2]]
     }
 
@@ -278,7 +282,7 @@ describe('useId', () => {
       'v:0-0-0 v:0-0-1' + // async component inside async setup
       '</div>'
     // assert different async resolution order does not affect id stable-ness
-    expect(await getOutput(() => factory(0, 16))).toBe(expected)
+    expect(await getOutput(async () => factory(0, 16))).toBe(expected)
     expect(await getOutput(() => factory(16, 0))).toBe(expected)
   })
 })
index 579bdd34685f4a40c81c2d1f62d9e92a8e5dcc8c..dc1d3ae1141843436f292093a51cdab4b07fbe9f 100644 (file)
@@ -124,6 +124,7 @@ export function defineAsyncComponent<
 
     setup() {
       const instance = currentInstance!
+      markAsyncBoundary(instance)
 
       // already resolved
       if (resolvedComp) {
@@ -160,8 +161,6 @@ export function defineAsyncComponent<
           })
       }
 
-      markAsyncBoundary(instance)
-
       const loaded = ref(false)
       const error = ref()
       const delayed = ref(!!delay)
index a660958841a69f561f4644756484abe0b571a3d7..9431ce6c4dd7bca3530d618f826059c00b631d3f 100644 (file)
@@ -93,6 +93,7 @@ import type { KeepAliveProps } from './components/KeepAlive'
 import type { BaseTransitionProps } from './components/BaseTransition'
 import type { DefineComponent } from './apiDefineComponent'
 import { markAsyncBoundary } from './helpers/useId'
+import { isAsyncWrapper } from './apiAsyncComponent'
 
 export type Data = Record<string, unknown>
 
@@ -866,7 +867,7 @@ function setupStatefulComponent(
 
     if (isPromise(setupResult)) {
       // async setup, mark as async boundary for useId()
-      markAsyncBoundary(instance)
+      if (!isAsyncWrapper(instance)) markAsyncBoundary(instance)
       setupResult.then(unsetCurrentInstance, unsetCurrentInstance)
       if (isSSR) {
         // return the promise so server-renderer can wait on it