From: Evan You Date: Wed, 24 Jul 2024 15:08:56 +0000 (+0800) Subject: fix(useId): properly mark async boundary for already resolved async component X-Git-Tag: v3.5.0-alpha.4~2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=cd281725781ada2ab279e919031ae307e146a9d9;p=thirdparty%2Fvuejs%2Fcore.git fix(useId): properly mark async boundary for already resolved async component --- diff --git a/packages/runtime-core/__tests__/helpers/useId.spec.ts b/packages/runtime-core/__tests__/helpers/useId.spec.ts index 17fdd24d01..564755e2fd 100644 --- a/packages/runtime-core/__tests__/helpers/useId.spec.ts +++ b/packages/runtime-core/__tests__/helpers/useId.spec.ts @@ -12,10 +12,11 @@ import { } from 'vue' import { renderToString } from '@vue/server-renderer' -type TestCaseFactory = () => [App, Promise[]] +type FactoryRes = [App, Promise[]] +type TestCaseFactory = () => FactoryRes | Promise 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 => { + ): Promise => { 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 '' // 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) }) }) diff --git a/packages/runtime-core/src/apiAsyncComponent.ts b/packages/runtime-core/src/apiAsyncComponent.ts index 579bdd3468..dc1d3ae114 100644 --- a/packages/runtime-core/src/apiAsyncComponent.ts +++ b/packages/runtime-core/src/apiAsyncComponent.ts @@ -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) diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index a660958841..9431ce6c4d 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -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 @@ -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