expect(serializeInner(root)).toBe('<!---->')
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)
+ })
})
let vnodeHook: VNodeHook | null | undefined
const { el, props } = initialVNode
const { bm, m, parent } = instance
+ const isAsyncWrapperVNode = isAsyncWrapper(initialVNode)
effect.allowRecurse = false
// beforeMount hook
invokeArrayFns(bm)
}
// onVnodeBeforeMount
- if ((vnodeHook = props && props.onVnodeBeforeMount)) {
+ if (
+ !isAsyncWrapperVNode &&
+ (vnodeHook = props && props.onVnodeBeforeMount)
+ ) {
invokeVNodeHook(vnodeHook, parent, initialVNode)
}
if (
}
}
- if (isAsyncWrapper(initialVNode)) {
+ if (isAsyncWrapperVNode) {
;(initialVNode.type as ComponentOptions).__asyncLoader!().then(
// note: we are moving the render call into an async callback,
// which means it won't track dependencies - but it's ok because
queuePostRenderEffect(m, parentSuspense)
}
// onVnodeMounted
- if ((vnodeHook = props && props.onVnodeMounted)) {
+ if (
+ !isAsyncWrapperVNode &&
+ (vnodeHook = props && props.onVnodeMounted)
+ ) {
const scopedInitialVNode = initialVNode
queuePostRenderEffect(
() => invokeVNodeHook(vnodeHook!, parent, scopedInitialVNode),
}
const shouldInvokeDirs = shapeFlag & ShapeFlags.ELEMENT && dirs
+ const shouldInvokeVnodeHook = !isAsyncWrapper(vnode)
let vnodeHook: VNodeHook | undefined | null
- if ((vnodeHook = props && props.onVnodeBeforeUnmount)) {
+ if (
+ shouldInvokeVnodeHook &&
+ (vnodeHook = props && props.onVnodeBeforeUnmount)
+ ) {
invokeVNodeHook(vnodeHook, parentComponent, vnode)
}
}
}
- if ((vnodeHook = props && props.onVnodeUnmounted) || shouldInvokeDirs) {
+ if (
+ (shouldInvokeVnodeHook &&
+ (vnodeHook = props && props.onVnodeUnmounted)) ||
+ shouldInvokeDirs
+ ) {
queuePostRenderEffect(() => {
vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode)
shouldInvokeDirs &&