Skip to content

Commit

Permalink
fix(useId): properly mark async boundary for already resolved async c…
Browse files Browse the repository at this point in the history
…omponent
  • Loading branch information
yyx990803 committed Jul 24, 2024
1 parent 2a55f22 commit cd28172
Show file tree
Hide file tree
Showing 3 changed files with 14 additions and 10 deletions.
18 changes: 11 additions & 7 deletions packages/runtime-core/__tests__/helpers/useId.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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()
Expand Down Expand Up @@ -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)
Expand All @@ -269,6 +270,9 @@ describe('useId', () => {
})
},
})

// the async component may have already been resolved
await AsyncInner.__asyncLoader()
return [app, [p1, p2]]
}

Expand All @@ -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)
})
})
3 changes: 1 addition & 2 deletions packages/runtime-core/src/apiAsyncComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ export function defineAsyncComponent<

setup() {
const instance = currentInstance!
markAsyncBoundary(instance)

// already resolved
if (resolvedComp) {
Expand Down Expand Up @@ -160,8 +161,6 @@ export function defineAsyncComponent<
})
}

markAsyncBoundary(instance)

const loaded = ref(false)
const error = ref()
const delayed = ref(!!delay)
Expand Down
3 changes: 2 additions & 1 deletion packages/runtime-core/src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down Expand Up @@ -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
Expand Down

0 comments on commit cd28172

Please sign in to comment.