fix(suspense): fix suspense regression for errored template component

fix #3857
This commit is contained in:
Evan You 2021-05-31 17:18:58 -04:00
parent c7efb967ca
commit 44996d1a0a
2 changed files with 54 additions and 8 deletions

View File

@ -632,6 +632,49 @@ describe('Suspense', () => {
expect(serializeInner(root)).toBe(`<div>oops</div>`) expect(serializeInner(root)).toBe(`<div>oops</div>`)
}) })
// #3857
test('error handling w/ template optimization', async () => {
const Async = {
async setup() {
throw new Error('oops')
}
}
const Comp = {
template: `
<div v-if="errorMessage">{{ errorMessage }}</div>
<Suspense v-else>
<div>
<Async />
</div>
<template #fallback>
<div>fallback</div>
</template>
</Suspense>
`,
components: { Async },
setup() {
const errorMessage = ref<string | null>(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(`<div>fallback</div>`)
await Promise.all(deps)
await nextTick()
expect(serializeInner(root)).toBe(`<div>oops</div>`)
})
it('combined usage (nested async + nested suspense + multiple deps)', async () => { it('combined usage (nested async + nested suspense + multiple deps)', async () => {
const msg = ref('nested msg') const msg = ref('nested msg')
const calls: number[] = [] const calls: number[] = []

View File

@ -1073,16 +1073,19 @@ function baseCreateRenderer(
const newVNode = newChildren[i] const newVNode = newChildren[i]
// Determine the container (parent element) for the patch. // Determine the container (parent element) for the patch.
const container = 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 // - In the case of a Fragment, we need to provide the actual parent
// of the Fragment itself so it can move its children. // of the Fragment itself so it can move its children.
oldVNode.type === Fragment || (oldVNode.type === Fragment ||
// - In the case of different nodes, there is going to be a replacement // - In the case of different nodes, there is going to be a replacement
// which also requires the correct parent container // which also requires the correct parent container
!isSameVNodeType(oldVNode, newVNode) || !isSameVNodeType(oldVNode, newVNode) ||
// - In the case of a component, it could contain anything. // - In the case of a component, it could contain anything.
oldVNode.shapeFlag & ShapeFlags.COMPONENT || oldVNode.shapeFlag & ShapeFlags.COMPONENT ||
oldVNode.shapeFlag & ShapeFlags.TELEPORT oldVNode.shapeFlag & ShapeFlags.TELEPORT)
? hostParentNode(oldVNode.el!)! ? hostParentNode(oldVNode.el)!
: // In other cases, the parent container is not actually used so we : // In other cases, the parent container is not actually used so we
// just pass the block element here to avoid a DOM parentNode call. // just pass the block element here to avoid a DOM parentNode call.
fallbackContainer fallbackContainer