mirror of https://github.com/vuejs/core.git
fix(suspense): handle suspense switching with nested suspense (#10184)
close #10098
This commit is contained in:
parent
411596c07b
commit
0f3da05ea2
|
@ -22,7 +22,7 @@ import {
|
|||
watch,
|
||||
watchEffect,
|
||||
} from '@vue/runtime-test'
|
||||
import { createApp, defineComponent } from 'vue'
|
||||
import { computed, createApp, defineComponent, inject, provide } from 'vue'
|
||||
import type { RawSlots } from 'packages/runtime-core/src/componentSlots'
|
||||
import { resetSuspenseId } from '../../src/components/Suspense'
|
||||
|
||||
|
@ -1039,6 +1039,99 @@ describe('Suspense', () => {
|
|||
expect(serializeInner(root)).toBe(`<div>foo<div>foo nested</div></div>`)
|
||||
})
|
||||
|
||||
// #10098
|
||||
test('switching branches w/ nested suspense', async () => {
|
||||
const RouterView = {
|
||||
setup(_: any, { slots }: any) {
|
||||
const route = inject('route') as any
|
||||
const depth = inject('depth', 0)
|
||||
provide('depth', depth + 1)
|
||||
return () => {
|
||||
const current = route.value[depth]
|
||||
return slots.default({ Component: current })[0]
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const OuterB = defineAsyncComponent({
|
||||
setup: () => {
|
||||
return () =>
|
||||
h(RouterView, null, {
|
||||
default: ({ Component }: any) => [
|
||||
h(Suspense, null, {
|
||||
default: () => h(Component),
|
||||
}),
|
||||
],
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
const InnerB = defineAsyncComponent({
|
||||
setup: () => {
|
||||
return () => h('div', 'innerB')
|
||||
},
|
||||
})
|
||||
|
||||
const OuterA = defineAsyncComponent({
|
||||
setup: () => {
|
||||
return () =>
|
||||
h(RouterView, null, {
|
||||
default: ({ Component }: any) => [
|
||||
h(Suspense, null, {
|
||||
default: () => h(Component),
|
||||
}),
|
||||
],
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
const InnerA = defineAsyncComponent({
|
||||
setup: () => {
|
||||
return () => h('div', 'innerA')
|
||||
},
|
||||
})
|
||||
|
||||
const toggle = ref(true)
|
||||
const route = computed(() => {
|
||||
return toggle.value ? [OuterA, InnerA] : [OuterB, InnerB]
|
||||
})
|
||||
|
||||
const Comp = {
|
||||
setup() {
|
||||
provide('route', route)
|
||||
return () =>
|
||||
h(RouterView, null, {
|
||||
default: ({ Component }: any) => [
|
||||
h(Suspense, null, {
|
||||
default: () => h(Component),
|
||||
}),
|
||||
],
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
const root = nodeOps.createElement('div')
|
||||
render(h(Comp), root)
|
||||
await Promise.all(deps)
|
||||
await nextTick()
|
||||
expect(serializeInner(root)).toBe(`<!---->`)
|
||||
|
||||
await Promise.all(deps)
|
||||
await nextTick()
|
||||
expect(serializeInner(root)).toBe(`<div>innerA</div>`)
|
||||
|
||||
deps.length = 0
|
||||
|
||||
toggle.value = false
|
||||
await nextTick()
|
||||
// toggle again
|
||||
toggle.value = true
|
||||
|
||||
await Promise.all(deps)
|
||||
await nextTick()
|
||||
expect(serializeInner(root)).toBe(`<div>innerA</div>`)
|
||||
})
|
||||
|
||||
test('branch switch to 3rd branch before resolve', async () => {
|
||||
const calls: string[] = []
|
||||
|
||||
|
|
|
@ -100,7 +100,9 @@ export const SuspenseImpl = {
|
|||
// it is necessary to skip the current patch to avoid multiple mounts
|
||||
// of inner components.
|
||||
if (parentSuspense && parentSuspense.deps > 0) {
|
||||
n2.suspense = n1.suspense
|
||||
n2.suspense = n1.suspense!
|
||||
n2.suspense.vnode = n2
|
||||
n2.el = n1.el
|
||||
return
|
||||
}
|
||||
patchSuspense(
|
||||
|
|
Loading…
Reference in New Issue