mirror of https://github.com/vuejs/core.git
fix(Suspense): handle switching away from kept-alive component before resolve
close #6416 using test from #6467
This commit is contained in:
parent
96aeb24d66
commit
aa0c13f637
|
@ -19,7 +19,8 @@ import {
|
||||||
shallowRef,
|
shallowRef,
|
||||||
SuspenseProps,
|
SuspenseProps,
|
||||||
resolveDynamicComponent,
|
resolveDynamicComponent,
|
||||||
Fragment
|
Fragment,
|
||||||
|
KeepAlive
|
||||||
} from '@vue/runtime-test'
|
} from '@vue/runtime-test'
|
||||||
import { createApp, defineComponent } from 'vue'
|
import { createApp, defineComponent } from 'vue'
|
||||||
import { type RawSlots } from 'packages/runtime-core/src/componentSlots'
|
import { type RawSlots } from 'packages/runtime-core/src/componentSlots'
|
||||||
|
@ -1638,6 +1639,55 @@ describe('Suspense', () => {
|
||||||
expect(serializeInner(root)).toBe(expected)
|
expect(serializeInner(root)).toBe(expected)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// #6416
|
||||||
|
test('KeepAlive with Suspense', async () => {
|
||||||
|
const Async = defineAsyncComponent({
|
||||||
|
render() {
|
||||||
|
return h('div', 'async')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const Sync = {
|
||||||
|
render() {
|
||||||
|
return h('div', 'sync')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const components = [Async, Sync]
|
||||||
|
const viewRef = ref(0)
|
||||||
|
const root = nodeOps.createElement('div')
|
||||||
|
const App = {
|
||||||
|
render() {
|
||||||
|
return h(KeepAlive, null, {
|
||||||
|
default: () => {
|
||||||
|
return h(Suspense, null, {
|
||||||
|
default: h(components[viewRef.value]),
|
||||||
|
fallback: h('div', 'Loading-dynamic-components')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render(h(App), root)
|
||||||
|
expect(serializeInner(root)).toBe(`<div>Loading-dynamic-components</div>`)
|
||||||
|
|
||||||
|
viewRef.value = 1
|
||||||
|
await nextTick()
|
||||||
|
expect(serializeInner(root)).toBe(`<div>sync</div>`)
|
||||||
|
|
||||||
|
viewRef.value = 0
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
expect(serializeInner(root)).toBe('<!---->')
|
||||||
|
|
||||||
|
await Promise.all(deps)
|
||||||
|
await nextTick()
|
||||||
|
// when async resolve,it should be <div>async</div>
|
||||||
|
expect(serializeInner(root)).toBe('<div>async</div>')
|
||||||
|
|
||||||
|
viewRef.value = 1
|
||||||
|
await nextTick() //TypeError: Cannot read properties of null (reading 'parentNode'),This has been fixed
|
||||||
|
expect(serializeInner(root)).toBe(`<div>sync</div>`)
|
||||||
|
})
|
||||||
|
|
||||||
describe('warnings', () => {
|
describe('warnings', () => {
|
||||||
// base function to check if a combination of slots warns or not
|
// base function to check if a combination of slots warns or not
|
||||||
function baseCheckWarn(
|
function baseCheckWarn(
|
||||||
|
|
|
@ -47,6 +47,9 @@ export interface SuspenseProps {
|
||||||
|
|
||||||
export const isSuspense = (type: any): boolean => type.__isSuspense
|
export const isSuspense = (type: any): boolean => type.__isSuspense
|
||||||
|
|
||||||
|
// incrementing unique id for every pending branch
|
||||||
|
let suspenseId = 0
|
||||||
|
|
||||||
// Suspense exposes a component-like API, and is treated like a component
|
// Suspense exposes a component-like API, and is treated like a component
|
||||||
// in the compiler, but internally it's a special built-in type that hooks
|
// in the compiler, but internally it's a special built-in type that hooks
|
||||||
// directly into the renderer.
|
// directly into the renderer.
|
||||||
|
@ -249,7 +252,8 @@ function patchSuspense(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// toggled before pending tree is resolved
|
// toggled before pending tree is resolved
|
||||||
suspense.pendingId++
|
// increment pending ID. this is used to invalidate async callbacks
|
||||||
|
suspense.pendingId = suspenseId++
|
||||||
if (isHydrating) {
|
if (isHydrating) {
|
||||||
// if toggled before hydration is finished, the current DOM tree is
|
// if toggled before hydration is finished, the current DOM tree is
|
||||||
// no longer valid. set it as the active branch so it will be unmounted
|
// no longer valid. set it as the active branch so it will be unmounted
|
||||||
|
@ -259,7 +263,6 @@ function patchSuspense(
|
||||||
} else {
|
} else {
|
||||||
unmount(pendingBranch, parentComponent, suspense)
|
unmount(pendingBranch, parentComponent, suspense)
|
||||||
}
|
}
|
||||||
// increment pending ID. this is used to invalidate async callbacks
|
|
||||||
// reset suspense state
|
// reset suspense state
|
||||||
suspense.deps = 0
|
suspense.deps = 0
|
||||||
// discard effects from pending branch
|
// discard effects from pending branch
|
||||||
|
@ -350,7 +353,11 @@ function patchSuspense(
|
||||||
triggerEvent(n2, 'onPending')
|
triggerEvent(n2, 'onPending')
|
||||||
// mount pending branch in off-dom container
|
// mount pending branch in off-dom container
|
||||||
suspense.pendingBranch = newBranch
|
suspense.pendingBranch = newBranch
|
||||||
suspense.pendingId++
|
if (newBranch.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
|
||||||
|
suspense.pendingId = newBranch.component!.suspenseId!
|
||||||
|
} else {
|
||||||
|
suspense.pendingId = suspenseId++
|
||||||
|
}
|
||||||
patch(
|
patch(
|
||||||
null,
|
null,
|
||||||
newBranch,
|
newBranch,
|
||||||
|
|
Loading…
Reference in New Issue