diff --git a/packages/runtime-core/__tests__/hydration.spec.ts b/packages/runtime-core/__tests__/hydration.spec.ts index efc0507d81e..ed1e228c4cf 100644 --- a/packages/runtime-core/__tests__/hydration.spec.ts +++ b/packages/runtime-core/__tests__/hydration.spec.ts @@ -688,6 +688,54 @@ describe('SSR hydration', () => { expect(container.innerHTML).toBe(`1`) }) + // #6638 + test('Suspense + async component', async () => { + let isSuspenseResolved = false + let isSuspenseResolvedInChild: any + const AsyncChild = defineAsyncComponent(() => + Promise.resolve( + defineComponent({ + setup() { + isSuspenseResolvedInChild = isSuspenseResolved + const count = ref(0) + return () => + h( + 'span', + { + onClick: () => { + count.value++ + }, + }, + count.value, + ) + }, + }), + ), + ) + const { vnode, container } = mountWithHydration('0', () => + h( + Suspense, + { + onResolve() { + isSuspenseResolved = true + }, + }, + () => h(AsyncChild), + ), + ) + expect(vnode.el).toBe(container.firstChild) + // wait for hydration to finish + await new Promise(r => setTimeout(r)) + + expect(isSuspenseResolvedInChild).toBe(false) + expect(isSuspenseResolved).toBe(true) + + // assert interaction + triggerEvent('click', container.querySelector('span')!) + await nextTick() + expect(container.innerHTML).toBe(`1`) + }) + test('Suspense (full integration)', async () => { const mountedCalls: number[] = [] const asyncDeps: Promise[] = [] diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 4452407c2a8..ddc46c70049 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -1276,7 +1276,7 @@ function baseCreateRenderer( const componentUpdateFn = () => { if (!instance.isMounted) { let vnodeHook: VNodeHook | null | undefined - const { el, props } = initialVNode + const { el, props, type } = initialVNode const { bm, m, parent } = instance const isAsyncWrapperVNode = isAsyncWrapper(initialVNode) @@ -1325,8 +1325,11 @@ function baseCreateRenderer( } } - if (isAsyncWrapperVNode) { - ;(initialVNode.type as ComponentOptions).__asyncLoader!().then( + if ( + isAsyncWrapperVNode && + !(type as ComponentOptions).__asyncResolved + ) { + ;(type as ComponentOptions).__asyncLoader!().then( // note: we are moving the render call into an async callback, // which means it won't track dependencies - but it's ok because // a server-rendered async wrapper is already in resolved state