--- /dev/null
+import {
+ h,
+ render,
+ nodeOps,
+ VNodeProps,
+ TestElement,
+ NodeTypes,
+ VNode
+} from '@vue/runtime-test'
+
+describe('renderer: vnode hooks', () => {
+ function assertHooks(hooks: VNodeProps, vnode1: VNode, vnode2: VNode) {
+ const root = nodeOps.createElement('div')
+ render(vnode1, root)
+ expect(hooks.onVnodeBeforeMount).toHaveBeenCalledWith(vnode1, null)
+ expect(hooks.onVnodeMounted).toHaveBeenCalledWith(vnode1, null)
+ expect(hooks.onVnodeBeforeUpdate).not.toHaveBeenCalled()
+ expect(hooks.onVnodeUpdated).not.toHaveBeenCalled()
+ expect(hooks.onVnodeBeforeUnmount).not.toHaveBeenCalled()
+ expect(hooks.onVnodeUnmounted).not.toHaveBeenCalled()
+
+ // update
+ render(vnode2, root)
+ expect(hooks.onVnodeBeforeMount).toHaveBeenCalledTimes(1)
+ expect(hooks.onVnodeMounted).toHaveBeenCalledTimes(1)
+ expect(hooks.onVnodeBeforeUpdate).toHaveBeenCalledWith(vnode2, vnode1)
+ expect(hooks.onVnodeUpdated).toHaveBeenCalledWith(vnode2, vnode1)
+ expect(hooks.onVnodeBeforeUnmount).not.toHaveBeenCalled()
+ expect(hooks.onVnodeUnmounted).not.toHaveBeenCalled()
+
+ // unmount
+ render(null, root)
+ expect(hooks.onVnodeBeforeMount).toHaveBeenCalledTimes(1)
+ expect(hooks.onVnodeMounted).toHaveBeenCalledTimes(1)
+ expect(hooks.onVnodeBeforeUpdate).toHaveBeenCalledTimes(1)
+ expect(hooks.onVnodeUpdated).toHaveBeenCalledTimes(1)
+ expect(hooks.onVnodeBeforeUnmount).toHaveBeenCalledWith(vnode2, null)
+ expect(hooks.onVnodeUnmounted).toHaveBeenCalledWith(vnode2, null)
+ }
+
+ test('should work on element', () => {
+ const hooks: VNodeProps = {
+ onVnodeBeforeMount: jest.fn(),
+ onVnodeMounted: jest.fn(),
+ onVnodeBeforeUpdate: jest.fn(vnode => {
+ expect((vnode.el as TestElement).children[0]).toMatchObject({
+ type: NodeTypes.TEXT,
+ text: 'foo'
+ })
+ }),
+ onVnodeUpdated: jest.fn(vnode => {
+ expect((vnode.el as TestElement).children[0]).toMatchObject({
+ type: NodeTypes.TEXT,
+ text: 'bar'
+ })
+ }),
+ onVnodeBeforeUnmount: jest.fn(),
+ onVnodeUnmounted: jest.fn()
+ }
+
+ assertHooks(hooks, h('div', hooks, 'foo'), h('div', hooks, 'bar'))
+ })
+
+ test('should work on component', () => {
+ const Comp = (props: { msg: string }) => props.msg
+
+ const hooks: VNodeProps = {
+ onVnodeBeforeMount: jest.fn(),
+ onVnodeMounted: jest.fn(),
+ onVnodeBeforeUpdate: jest.fn(vnode => {
+ expect(vnode.el as TestElement).toMatchObject({
+ type: NodeTypes.TEXT,
+ text: 'foo'
+ })
+ }),
+ onVnodeUpdated: jest.fn(vnode => {
+ expect(vnode.el as TestElement).toMatchObject({
+ type: NodeTypes.TEXT,
+ text: 'bar'
+ })
+ }),
+ onVnodeBeforeUnmount: jest.fn(),
+ onVnodeUnmounted: jest.fn()
+ }
+
+ assertHooks(
+ hooks,
+ h(Comp, {
+ ...hooks,
+ msg: 'foo'
+ }),
+ h(Comp, {
+ ...hooks,
+ msg: 'bar'
+ })
+ )
+ })
+})
const nextTree = renderComponentRoot(instance)
const prevTree = instance.subTree
instance.subTree = nextTree
+ next.el = vnode.el
// beforeUpdate hook
if (bu !== null) {
invokeHooks(bu)
setRef(ref, null, parentComponent, null)
}
+ if ((vnodeHook = props && props.onVnodeBeforeUnmount) != null) {
+ invokeVNodeHook(vnodeHook, parentComponent, vnode)
+ }
+
if (shapeFlag & ShapeFlags.COMPONENT) {
if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
;(parentComponent!.sink as KeepAliveSink).deactivate(vnode)
} else {
unmountComponent(vnode.component!, parentSuspense, doRemove)
}
- return
- }
-
- if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
- vnode.suspense!.unmount(parentSuspense, doRemove)
- return
- }
+ } else {
+ if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
+ vnode.suspense!.unmount(parentSuspense, doRemove)
+ return
+ }
- if ((vnodeHook = props && props.onVnodeBeforeUnmount) != null) {
- invokeVNodeHook(vnodeHook, parentComponent, vnode)
- }
- if (shouldInvokeDirs) {
- invokeDirectiveHook(vnode, null, parentComponent, 'beforeUnmount')
- }
+ if (shouldInvokeDirs) {
+ invokeDirectiveHook(vnode, null, parentComponent, 'beforeUnmount')
+ }
- if (dynamicChildren != null) {
- // fast path for block nodes: only need to unmount dynamic children.
- unmountChildren(dynamicChildren, parentComponent, parentSuspense)
- } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
- unmountChildren(children as HostVNode[], parentComponent, parentSuspense)
- }
+ if (dynamicChildren != null) {
+ // fast path for block nodes: only need to unmount dynamic children.
+ unmountChildren(dynamicChildren, parentComponent, parentSuspense)
+ } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
+ unmountChildren(
+ children as HostVNode[],
+ parentComponent,
+ parentSuspense
+ )
+ }
- if (doRemove) {
- remove(vnode)
+ if (doRemove) {
+ remove(vnode)
+ }
}
if (