Skip to content

Commit cd28172

Browse files
committed
fix(useId): properly mark async boundary for already resolved async component
1 parent 2a55f22 commit cd28172

File tree

3 files changed

+14
-10
lines changed

3 files changed

+14
-10
lines changed

packages/runtime-core/__tests__/helpers/useId.spec.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ import {
1212
} from 'vue'
1313
import { renderToString } from '@vue/server-renderer'
1414

15-
type TestCaseFactory = () => [App, Promise<any>[]]
15+
type FactoryRes = [App, Promise<any>[]]
16+
type TestCaseFactory = () => FactoryRes | Promise<FactoryRes>
1617

1718
async function runOnClient(factory: TestCaseFactory) {
18-
const [app, deps] = factory()
19+
const [app, deps] = await factory()
1920
const root = document.createElement('div')
2021
app.mount(root)
2122
await Promise.all(deps)
@@ -24,7 +25,7 @@ async function runOnClient(factory: TestCaseFactory) {
2425
}
2526

2627
async function runOnServer(factory: TestCaseFactory) {
27-
const [app, _] = factory()
28+
const [app, _] = await factory()
2829
return (await renderToString(app))
2930
.replace(/<!--[\[\]]-->/g, '') // remove fragment wrappers
3031
.trim()
@@ -240,11 +241,11 @@ describe('useId', () => {
240241
expect(await getOutput(() => factory())).toBe(expected)
241242
})
242243

243-
test('async component inside async setup', async () => {
244-
const factory = (
244+
test('async component inside async setup, already resolved', async () => {
245+
const factory = async (
245246
delay1: number,
246247
delay2: number,
247-
): ReturnType<TestCaseFactory> => {
248+
): Promise<FactoryRes> => {
248249
const p1 = promiseWithDelay(null, delay1)
249250
const p2 = promiseWithDelay(BasicComponentWithUseId, delay2)
250251
const AsyncInner = defineAsyncComponent(() => p2)
@@ -269,6 +270,9 @@ describe('useId', () => {
269270
})
270271
},
271272
})
273+
274+
// the async component may have already been resolved
275+
await AsyncInner.__asyncLoader()
272276
return [app, [p1, p2]]
273277
}
274278

@@ -278,7 +282,7 @@ describe('useId', () => {
278282
'v:0-0-0 v:0-0-1' + // async component inside async setup
279283
'</div>'
280284
// assert different async resolution order does not affect id stable-ness
281-
expect(await getOutput(() => factory(0, 16))).toBe(expected)
285+
expect(await getOutput(async () => factory(0, 16))).toBe(expected)
282286
expect(await getOutput(() => factory(16, 0))).toBe(expected)
283287
})
284288
})

packages/runtime-core/src/apiAsyncComponent.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ export function defineAsyncComponent<
124124

125125
setup() {
126126
const instance = currentInstance!
127+
markAsyncBoundary(instance)
127128

128129
// already resolved
129130
if (resolvedComp) {
@@ -160,8 +161,6 @@ export function defineAsyncComponent<
160161
})
161162
}
162163

163-
markAsyncBoundary(instance)
164-
165164
const loaded = ref(false)
166165
const error = ref()
167166
const delayed = ref(!!delay)

packages/runtime-core/src/component.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ import type { KeepAliveProps } from './components/KeepAlive'
9393
import type { BaseTransitionProps } from './components/BaseTransition'
9494
import type { DefineComponent } from './apiDefineComponent'
9595
import { markAsyncBoundary } from './helpers/useId'
96+
import { isAsyncWrapper } from './apiAsyncComponent'
9697

9798
export type Data = Record<string, unknown>
9899

@@ -866,7 +867,7 @@ function setupStatefulComponent(
866867

867868
if (isPromise(setupResult)) {
868869
// async setup, mark as async boundary for useId()
869-
markAsyncBoundary(instance)
870+
if (!isAsyncWrapper(instance)) markAsyncBoundary(instance)
870871
setupResult.then(unsetCurrentInstance, unsetCurrentInstance)
871872
if (isSSR) {
872873
// return the promise so server-renderer can wait on it

0 commit comments

Comments
 (0)