Skip to content

Commit cd2d984

Browse files
authored
fix(runtime-core): vnode hooks should not be called on async wrapper (#4349)
fix #4346
1 parent 3201224 commit cd2d984

File tree

2 files changed

+75
-5
lines changed

2 files changed

+75
-5
lines changed

packages/runtime-core/__tests__/apiAsyncComponent.spec.ts

+55
Original file line numberDiff line numberDiff line change
@@ -744,4 +744,59 @@ describe('api: defineAsyncComponent', () => {
744744
expect(serializeInner(root)).toBe('<!---->')
745745
expect(fooRef.value).toBe(null)
746746
})
747+
748+
test('vnode hooks on async wrapper', async () => {
749+
let resolve: (comp: Component) => void
750+
const Foo = defineAsyncComponent(
751+
() =>
752+
new Promise(r => {
753+
resolve = r as any
754+
})
755+
)
756+
const updater = ref(0)
757+
758+
const vnodeHooks = {
759+
onVnodeBeforeMount: jest.fn(),
760+
onVnodeMounted: jest.fn(),
761+
onVnodeBeforeUpdate: jest.fn(),
762+
onVnodeUpdated: jest.fn(),
763+
onVnodeBeforeUnmount: jest.fn(),
764+
onVnodeUnmounted: jest.fn()
765+
}
766+
767+
const toggle = ref(true)
768+
769+
const root = nodeOps.createElement('div')
770+
createApp({
771+
render: () => (toggle.value ? [h(Foo, vnodeHooks), updater.value] : null)
772+
}).mount(root)
773+
774+
expect(serializeInner(root)).toBe('<!---->0')
775+
776+
resolve!({
777+
data() {
778+
return {
779+
id: 'foo'
780+
}
781+
},
782+
render: () => 'resolved'
783+
})
784+
785+
await timeout()
786+
expect(serializeInner(root)).toBe('resolved0')
787+
expect(vnodeHooks.onVnodeBeforeMount).toHaveBeenCalledTimes(1)
788+
expect(vnodeHooks.onVnodeMounted).toHaveBeenCalledTimes(1)
789+
790+
updater.value++
791+
await nextTick()
792+
expect(serializeInner(root)).toBe('resolved1')
793+
expect(vnodeHooks.onVnodeBeforeUpdate).toHaveBeenCalledTimes(1)
794+
expect(vnodeHooks.onVnodeUpdated).toHaveBeenCalledTimes(1)
795+
796+
toggle.value = false
797+
await nextTick()
798+
expect(serializeInner(root)).toBe('<!---->')
799+
expect(vnodeHooks.onVnodeBeforeUnmount).toHaveBeenCalledTimes(1)
800+
expect(vnodeHooks.onVnodeUnmounted).toHaveBeenCalledTimes(1)
801+
})
747802
})

packages/runtime-core/src/renderer.ts

+20-5
Original file line numberDiff line numberDiff line change
@@ -1316,14 +1316,18 @@ function baseCreateRenderer(
13161316
let vnodeHook: VNodeHook | null | undefined
13171317
const { el, props } = initialVNode
13181318
const { bm, m, parent } = instance
1319+
const isAsyncWrapperVNode = isAsyncWrapper(initialVNode)
13191320

13201321
effect.allowRecurse = false
13211322
// beforeMount hook
13221323
if (bm) {
13231324
invokeArrayFns(bm)
13241325
}
13251326
// onVnodeBeforeMount
1326-
if ((vnodeHook = props && props.onVnodeBeforeMount)) {
1327+
if (
1328+
!isAsyncWrapperVNode &&
1329+
(vnodeHook = props && props.onVnodeBeforeMount)
1330+
) {
13271331
invokeVNodeHook(vnodeHook, parent, initialVNode)
13281332
}
13291333
if (
@@ -1359,7 +1363,7 @@ function baseCreateRenderer(
13591363
}
13601364
}
13611365

1362-
if (isAsyncWrapper(initialVNode)) {
1366+
if (isAsyncWrapperVNode) {
13631367
;(initialVNode.type as ComponentOptions).__asyncLoader!().then(
13641368
// note: we are moving the render call into an async callback,
13651369
// which means it won't track dependencies - but it's ok because
@@ -1400,7 +1404,10 @@ function baseCreateRenderer(
14001404
queuePostRenderEffect(m, parentSuspense)
14011405
}
14021406
// onVnodeMounted
1403-
if ((vnodeHook = props && props.onVnodeMounted)) {
1407+
if (
1408+
!isAsyncWrapperVNode &&
1409+
(vnodeHook = props && props.onVnodeMounted)
1410+
) {
14041411
const scopedInitialVNode = initialVNode
14051412
queuePostRenderEffect(
14061413
() => invokeVNodeHook(vnodeHook!, parent, scopedInitialVNode),
@@ -2085,9 +2092,13 @@ function baseCreateRenderer(
20852092
}
20862093

20872094
const shouldInvokeDirs = shapeFlag & ShapeFlags.ELEMENT && dirs
2095+
const shouldInvokeVnodeHook = !isAsyncWrapper(vnode)
20882096

20892097
let vnodeHook: VNodeHook | undefined | null
2090-
if ((vnodeHook = props && props.onVnodeBeforeUnmount)) {
2098+
if (
2099+
shouldInvokeVnodeHook &&
2100+
(vnodeHook = props && props.onVnodeBeforeUnmount)
2101+
) {
20912102
invokeVNodeHook(vnodeHook, parentComponent, vnode)
20922103
}
20932104

@@ -2140,7 +2151,11 @@ function baseCreateRenderer(
21402151
}
21412152
}
21422153

2143-
if ((vnodeHook = props && props.onVnodeUnmounted) || shouldInvokeDirs) {
2154+
if (
2155+
(shouldInvokeVnodeHook &&
2156+
(vnodeHook = props && props.onVnodeUnmounted)) ||
2157+
shouldInvokeDirs
2158+
) {
21442159
queuePostRenderEffect(() => {
21452160
vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode)
21462161
shouldInvokeDirs &&

0 commit comments

Comments
 (0)