Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 140 additions & 1 deletion packages/runtime-core/__tests__/components/Suspense.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@ import {
KeepAlive,
Suspense,
type SuspenseProps,
createBlock,
createCommentVNode,
createElementBlock,
h,
nextTick,
nodeOps,
onErrorCaptured,
onMounted,
onUnmounted,
openBlock,
ref,
render,
resolveDynamicComponent,
Expand All @@ -23,9 +26,17 @@ import {
watch,
watchEffect,
} from '@vue/runtime-test'
import { computed, createApp, defineComponent, inject, provide } from 'vue'
import {
computed,
createApp,
defineAsyncComponent as defineAsyncComp,
defineComponent,
inject,
provide,
} from 'vue'
import type { RawSlots } from 'packages/runtime-core/src/componentSlots'
import { resetSuspenseId } from '../../src/components/Suspense'
import { PatchFlags } from '@vue/shared'

describe('Suspense', () => {
const deps: Promise<any>[] = []
Expand Down Expand Up @@ -2161,6 +2172,134 @@ describe('Suspense', () => {
await Promise.all(deps)
})

// #12920
test('unmount Suspense after async child (with defineAsyncComponent) self-triggered update', async () => {
const Comp = defineComponent({
setup() {
const show = ref(true)
onMounted(() => {
// trigger update
show.value = !show.value
})
return () =>
show.value
? (openBlock(), createElementBlock('div', { key: 0 }, 'show'))
: (openBlock(), createElementBlock('div', { key: 1 }, 'hidden'))
},
})

const AsyncComp = defineAsyncComp(() => {
const p = new Promise(resolve => {
resolve(Comp)
})
deps.push(p.then(() => Promise.resolve()))
return p as any
})

const toggle = ref(true)
const root = nodeOps.createElement('div')
const App = {
render() {
return (
openBlock(),
createElementBlock(
Fragment,
null,
[
h('h1', null, toggle.value),
toggle.value
? (openBlock(),
createBlock(
Suspense,
{ key: 0 },
{
default: h(AsyncComp),
},
))
: createCommentVNode('v-if', true),
],
PatchFlags.STABLE_FRAGMENT,
)
)
},
}
render(h(App), root)
expect(serializeInner(root)).toBe(`<h1>true</h1><!---->`)

await Promise.all(deps)
await nextTick()
await nextTick()
expect(serializeInner(root)).toBe(`<h1>true</h1><div>show</div>`)

await nextTick()
expect(serializeInner(root)).toBe(`<h1>true</h1><div>hidden</div>`)

// unmount suspense
toggle.value = false
await Promise.all(deps)
await nextTick()
expect(serializeInner(root)).toBe(`<h1>true</h1><!--v-if-->`)
})

test('unmount Suspense after async child (with async setup) self-triggered update', async () => {
const AsyncComp = defineComponent({
async setup() {
const show = ref(true)
onMounted(() => {
// trigger update
show.value = !show.value
})
const p = new Promise(r => setTimeout(r, 1))
// extra tick needed for Node 12+
deps.push(p.then(() => Promise.resolve()))
return () =>
show.value
? (openBlock(), createElementBlock('div', { key: 0 }, 'show'))
: (openBlock(), createElementBlock('div', { key: 1 }, 'hidden'))
},
})

const toggle = ref(true)
const root = nodeOps.createElement('div')
const App = {
render() {
return (
openBlock(),
createElementBlock(
Fragment,
null,
[
h('h1', null, toggle.value),
toggle.value
? (openBlock(),
createBlock(
Suspense,
{ key: 0 },
{
default: h(AsyncComp),
},
))
: createCommentVNode('v-if', true),
],
PatchFlags.STABLE_FRAGMENT,
)
)
},
}
render(h(App), root)
expect(serializeInner(root)).toBe(`<h1>true</h1><!---->`)

await Promise.all(deps)
await nextTick()
expect(serializeInner(root)).toBe(`<h1>true</h1><div>hidden</div>`)

// unmount suspense
toggle.value = false
await Promise.all(deps)
await nextTick()
expect(serializeInner(root)).toBe(`<h1>true</h1><!--v-if-->`)
})

describe('warnings', () => {
// base function to check if a combination of slots warns or not
function baseCheckWarn(
Expand Down
8 changes: 6 additions & 2 deletions packages/runtime-core/src/componentRenderUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -451,13 +451,13 @@ function hasPropsChanged(
}

export function updateHOCHostEl(
{ vnode, parent }: ComponentInternalInstance,
{ vnode, parent, suspense }: ComponentInternalInstance,
el: typeof vnode.el, // HostNode
): void {
while (parent) {
const root = parent.subTree
if (root.suspense && root.suspense.activeBranch === vnode) {
root.el = vnode.el
root.suspense.vnode.el = root.el = vnode.el
}
if (root === vnode) {
;(vnode = parent.vnode).el = el
Expand All @@ -466,4 +466,8 @@ export function updateHOCHostEl(
break
}
}
// also update suspense vnode el
if (suspense && suspense.activeBranch === vnode) {
suspense.vnode.el = el
}
}