From: Evan You Date: Mon, 31 May 2021 21:18:58 +0000 (-0400) Subject: fix(suspense): fix suspense regression for errored template component X-Git-Tag: v3.1.0-beta.7~6 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=44996d1a0a2de1bc6b3abfac6b2b8b3c969d4e01;p=thirdparty%2Fvuejs%2Fcore.git fix(suspense): fix suspense regression for errored template component fix #3857 --- diff --git a/packages/runtime-core/__tests__/components/Suspense.spec.ts b/packages/runtime-core/__tests__/components/Suspense.spec.ts index bf209007d9..4228bc8401 100644 --- a/packages/runtime-core/__tests__/components/Suspense.spec.ts +++ b/packages/runtime-core/__tests__/components/Suspense.spec.ts @@ -632,6 +632,49 @@ describe('Suspense', () => { expect(serializeInner(root)).toBe(`
oops
`) }) + // #3857 + test('error handling w/ template optimization', async () => { + const Async = { + async setup() { + throw new Error('oops') + } + } + + const Comp = { + template: ` +
{{ errorMessage }}
+ +
+ +
+ +
+ `, + components: { Async }, + setup() { + const errorMessage = ref(null) + onErrorCaptured(err => { + errorMessage.value = + err instanceof Error + ? err.message + : `A non-Error value thrown: ${err}` + return false + }) + return { errorMessage } + } + } + + const root = nodeOps.createElement('div') + render(h(Comp), root) + expect(serializeInner(root)).toBe(`
fallback
`) + + await Promise.all(deps) + await nextTick() + expect(serializeInner(root)).toBe(`
oops
`) + }) + it('combined usage (nested async + nested suspense + multiple deps)', async () => { const msg = ref('nested msg') const calls: number[] = [] diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 36291a24f4..e190669951 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -1073,16 +1073,19 @@ function baseCreateRenderer( const newVNode = newChildren[i] // Determine the container (parent element) for the patch. const container = + // oldVNode may be an errored async setup() component inside Suspense + // which will not have a mounted element + oldVNode.el && // - In the case of a Fragment, we need to provide the actual parent // of the Fragment itself so it can move its children. - oldVNode.type === Fragment || - // - In the case of different nodes, there is going to be a replacement - // which also requires the correct parent container - !isSameVNodeType(oldVNode, newVNode) || - // - In the case of a component, it could contain anything. - oldVNode.shapeFlag & ShapeFlags.COMPONENT || - oldVNode.shapeFlag & ShapeFlags.TELEPORT - ? hostParentNode(oldVNode.el!)! + (oldVNode.type === Fragment || + // - In the case of different nodes, there is going to be a replacement + // which also requires the correct parent container + !isSameVNodeType(oldVNode, newVNode) || + // - In the case of a component, it could contain anything. + oldVNode.shapeFlag & ShapeFlags.COMPONENT || + oldVNode.shapeFlag & ShapeFlags.TELEPORT) + ? hostParentNode(oldVNode.el)! : // In other cases, the parent container is not actually used so we // just pass the block element here to avoid a DOM parentNode call. fallbackContainer