fix(runtime-core): vnode hooks should not be called on async wrapper (#4349)

fix #4346
This commit is contained in:
HcySunYang 2021-08-17 03:35:50 +08:00 committed by GitHub
parent 3201224ecb
commit cd2d98499e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 75 additions and 5 deletions

View File

@ -744,4 +744,59 @@ describe('api: defineAsyncComponent', () => {
expect(serializeInner(root)).toBe('<!---->') expect(serializeInner(root)).toBe('<!---->')
expect(fooRef.value).toBe(null) expect(fooRef.value).toBe(null)
}) })
test('vnode hooks on async wrapper', async () => {
let resolve: (comp: Component) => void
const Foo = defineAsyncComponent(
() =>
new Promise(r => {
resolve = r as any
})
)
const updater = ref(0)
const vnodeHooks = {
onVnodeBeforeMount: jest.fn(),
onVnodeMounted: jest.fn(),
onVnodeBeforeUpdate: jest.fn(),
onVnodeUpdated: jest.fn(),
onVnodeBeforeUnmount: jest.fn(),
onVnodeUnmounted: jest.fn()
}
const toggle = ref(true)
const root = nodeOps.createElement('div')
createApp({
render: () => (toggle.value ? [h(Foo, vnodeHooks), updater.value] : null)
}).mount(root)
expect(serializeInner(root)).toBe('<!---->0')
resolve!({
data() {
return {
id: 'foo'
}
},
render: () => 'resolved'
})
await timeout()
expect(serializeInner(root)).toBe('resolved0')
expect(vnodeHooks.onVnodeBeforeMount).toHaveBeenCalledTimes(1)
expect(vnodeHooks.onVnodeMounted).toHaveBeenCalledTimes(1)
updater.value++
await nextTick()
expect(serializeInner(root)).toBe('resolved1')
expect(vnodeHooks.onVnodeBeforeUpdate).toHaveBeenCalledTimes(1)
expect(vnodeHooks.onVnodeUpdated).toHaveBeenCalledTimes(1)
toggle.value = false
await nextTick()
expect(serializeInner(root)).toBe('<!---->')
expect(vnodeHooks.onVnodeBeforeUnmount).toHaveBeenCalledTimes(1)
expect(vnodeHooks.onVnodeUnmounted).toHaveBeenCalledTimes(1)
})
}) })

View File

@ -1316,6 +1316,7 @@ function baseCreateRenderer(
let vnodeHook: VNodeHook | null | undefined let vnodeHook: VNodeHook | null | undefined
const { el, props } = initialVNode const { el, props } = initialVNode
const { bm, m, parent } = instance const { bm, m, parent } = instance
const isAsyncWrapperVNode = isAsyncWrapper(initialVNode)
effect.allowRecurse = false effect.allowRecurse = false
// beforeMount hook // beforeMount hook
@ -1323,7 +1324,10 @@ function baseCreateRenderer(
invokeArrayFns(bm) invokeArrayFns(bm)
} }
// onVnodeBeforeMount // onVnodeBeforeMount
if ((vnodeHook = props && props.onVnodeBeforeMount)) { if (
!isAsyncWrapperVNode &&
(vnodeHook = props && props.onVnodeBeforeMount)
) {
invokeVNodeHook(vnodeHook, parent, initialVNode) invokeVNodeHook(vnodeHook, parent, initialVNode)
} }
if ( if (
@ -1359,7 +1363,7 @@ function baseCreateRenderer(
} }
} }
if (isAsyncWrapper(initialVNode)) { if (isAsyncWrapperVNode) {
;(initialVNode.type as ComponentOptions).__asyncLoader!().then( ;(initialVNode.type as ComponentOptions).__asyncLoader!().then(
// note: we are moving the render call into an async callback, // note: we are moving the render call into an async callback,
// which means it won't track dependencies - but it's ok because // which means it won't track dependencies - but it's ok because
@ -1400,7 +1404,10 @@ function baseCreateRenderer(
queuePostRenderEffect(m, parentSuspense) queuePostRenderEffect(m, parentSuspense)
} }
// onVnodeMounted // onVnodeMounted
if ((vnodeHook = props && props.onVnodeMounted)) { if (
!isAsyncWrapperVNode &&
(vnodeHook = props && props.onVnodeMounted)
) {
const scopedInitialVNode = initialVNode const scopedInitialVNode = initialVNode
queuePostRenderEffect( queuePostRenderEffect(
() => invokeVNodeHook(vnodeHook!, parent, scopedInitialVNode), () => invokeVNodeHook(vnodeHook!, parent, scopedInitialVNode),
@ -2085,9 +2092,13 @@ function baseCreateRenderer(
} }
const shouldInvokeDirs = shapeFlag & ShapeFlags.ELEMENT && dirs const shouldInvokeDirs = shapeFlag & ShapeFlags.ELEMENT && dirs
const shouldInvokeVnodeHook = !isAsyncWrapper(vnode)
let vnodeHook: VNodeHook | undefined | null let vnodeHook: VNodeHook | undefined | null
if ((vnodeHook = props && props.onVnodeBeforeUnmount)) { if (
shouldInvokeVnodeHook &&
(vnodeHook = props && props.onVnodeBeforeUnmount)
) {
invokeVNodeHook(vnodeHook, parentComponent, vnode) invokeVNodeHook(vnodeHook, parentComponent, vnode)
} }
@ -2140,7 +2151,11 @@ function baseCreateRenderer(
} }
} }
if ((vnodeHook = props && props.onVnodeUnmounted) || shouldInvokeDirs) { if (
(shouldInvokeVnodeHook &&
(vnodeHook = props && props.onVnodeUnmounted)) ||
shouldInvokeDirs
) {
queuePostRenderEffect(() => { queuePostRenderEffect(() => {
vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode) vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode)
shouldInvokeDirs && shouldInvokeDirs &&