fix(Suspense): emit initial fallback and pending events (#3965)
Fix #3964
This commit is contained in:
parent
43e2a72900
commit
ab6e927041
@ -69,6 +69,70 @@ describe('Suspense', () => {
|
|||||||
expect(serializeInner(root)).toBe(`<div>async</div>`)
|
expect(serializeInner(root)).toBe(`<div>async</div>`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('emits events', async () => {
|
||||||
|
const Async = defineAsyncComponent({
|
||||||
|
render() {
|
||||||
|
return h('div', 'async')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const onFallback = jest.fn()
|
||||||
|
const onResolve = jest.fn()
|
||||||
|
const onPending = jest.fn()
|
||||||
|
|
||||||
|
const show = ref(true)
|
||||||
|
const Comp = {
|
||||||
|
setup() {
|
||||||
|
return () =>
|
||||||
|
h(
|
||||||
|
Suspense,
|
||||||
|
{
|
||||||
|
onFallback,
|
||||||
|
onResolve,
|
||||||
|
onPending,
|
||||||
|
// force displaying the fallback right away
|
||||||
|
timeout: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
default: () => (show.value ? h(Async) : null),
|
||||||
|
fallback: h('div', 'fallback')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const root = nodeOps.createElement('div')
|
||||||
|
render(h(Comp), root)
|
||||||
|
expect(onFallback).toHaveBeenCalledTimes(1)
|
||||||
|
expect(onPending).toHaveBeenCalledTimes(1)
|
||||||
|
expect(onResolve).toHaveBeenCalledTimes(0)
|
||||||
|
|
||||||
|
await Promise.all(deps)
|
||||||
|
await nextTick()
|
||||||
|
expect(onFallback).toHaveBeenCalledTimes(1)
|
||||||
|
expect(onPending).toHaveBeenCalledTimes(1)
|
||||||
|
expect(onResolve).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
|
show.value = false
|
||||||
|
await nextTick()
|
||||||
|
expect(onFallback).toHaveBeenCalledTimes(1)
|
||||||
|
expect(onPending).toHaveBeenCalledTimes(2)
|
||||||
|
expect(onResolve).toHaveBeenCalledTimes(2)
|
||||||
|
|
||||||
|
deps.length = 0
|
||||||
|
show.value = true
|
||||||
|
await nextTick()
|
||||||
|
expect(onFallback).toHaveBeenCalledTimes(2)
|
||||||
|
expect(onPending).toHaveBeenCalledTimes(3)
|
||||||
|
expect(onResolve).toHaveBeenCalledTimes(2)
|
||||||
|
|
||||||
|
await Promise.all(deps)
|
||||||
|
await nextTick()
|
||||||
|
expect(onFallback).toHaveBeenCalledTimes(2)
|
||||||
|
expect(onPending).toHaveBeenCalledTimes(3)
|
||||||
|
expect(onResolve).toHaveBeenCalledTimes(3)
|
||||||
|
})
|
||||||
|
|
||||||
test('nested async deps', async () => {
|
test('nested async deps', async () => {
|
||||||
const calls: string[] = []
|
const calls: string[] = []
|
||||||
|
|
||||||
|
@ -94,6 +94,16 @@ export const Suspense = ((__FEATURE_SUSPENSE__
|
|||||||
new (): { $props: VNodeProps & SuspenseProps }
|
new (): { $props: VNodeProps & SuspenseProps }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function triggerEvent(
|
||||||
|
vnode: VNode,
|
||||||
|
name: 'onResolve' | 'onPending' | 'onFallback'
|
||||||
|
) {
|
||||||
|
const eventListener = vnode.props && vnode.props[name]
|
||||||
|
if (isFunction(eventListener)) {
|
||||||
|
eventListener()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function mountSuspense(
|
function mountSuspense(
|
||||||
vnode: VNode,
|
vnode: VNode,
|
||||||
container: RendererElement,
|
container: RendererElement,
|
||||||
@ -137,6 +147,10 @@ function mountSuspense(
|
|||||||
// now check if we have encountered any async deps
|
// now check if we have encountered any async deps
|
||||||
if (suspense.deps > 0) {
|
if (suspense.deps > 0) {
|
||||||
// has async
|
// has async
|
||||||
|
// invoke @fallback event
|
||||||
|
triggerEvent(vnode, 'onPending')
|
||||||
|
triggerEvent(vnode, 'onFallback')
|
||||||
|
|
||||||
// mount the fallback tree
|
// mount the fallback tree
|
||||||
patch(
|
patch(
|
||||||
null,
|
null,
|
||||||
@ -304,10 +318,7 @@ function patchSuspense(
|
|||||||
} else {
|
} else {
|
||||||
// root node toggled
|
// root node toggled
|
||||||
// invoke @pending event
|
// invoke @pending event
|
||||||
const onPending = n2.props && n2.props.onPending
|
triggerEvent(n2, 'onPending')
|
||||||
if (isFunction(onPending)) {
|
|
||||||
onPending()
|
|
||||||
}
|
|
||||||
// mount pending branch in off-dom container
|
// mount pending branch in off-dom container
|
||||||
suspense.pendingBranch = newBranch
|
suspense.pendingBranch = newBranch
|
||||||
suspense.pendingId++
|
suspense.pendingId++
|
||||||
@ -501,10 +512,7 @@ function createSuspenseBoundary(
|
|||||||
suspense.effects = []
|
suspense.effects = []
|
||||||
|
|
||||||
// invoke @resolve event
|
// invoke @resolve event
|
||||||
const onResolve = vnode.props && vnode.props.onResolve
|
triggerEvent(vnode, 'onResolve')
|
||||||
if (isFunction(onResolve)) {
|
|
||||||
onResolve()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
fallback(fallbackVNode) {
|
fallback(fallbackVNode) {
|
||||||
@ -521,10 +529,7 @@ function createSuspenseBoundary(
|
|||||||
} = suspense
|
} = suspense
|
||||||
|
|
||||||
// invoke @fallback event
|
// invoke @fallback event
|
||||||
const onFallback = vnode.props && vnode.props.onFallback
|
triggerEvent(vnode, 'onFallback')
|
||||||
if (isFunction(onFallback)) {
|
|
||||||
onFallback()
|
|
||||||
}
|
|
||||||
|
|
||||||
const anchor = next(activeBranch!)
|
const anchor = next(activeBranch!)
|
||||||
const mountFallback = () => {
|
const mountFallback = () => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user