fix(suspense): fix suspense regression for errored template component
fix #3857
This commit is contained in:
parent
c7efb967ca
commit
44996d1a0a
@ -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[] = []
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user