From 811f28a7d11eedea79ba10c99d1b4e25060e2d92 Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 18 Mar 2020 16:35:04 -0400 Subject: [PATCH] test(runtime-core): tests for vnode hooks --- .../runtime-core/__tests__/vnodeHooks.spec.ts | 98 +++++++++++++++++++ packages/runtime-core/src/renderer.ts | 47 +++++---- 2 files changed, 124 insertions(+), 21 deletions(-) create mode 100644 packages/runtime-core/__tests__/vnodeHooks.spec.ts diff --git a/packages/runtime-core/__tests__/vnodeHooks.spec.ts b/packages/runtime-core/__tests__/vnodeHooks.spec.ts new file mode 100644 index 000000000..44ed34373 --- /dev/null +++ b/packages/runtime-core/__tests__/vnodeHooks.spec.ts @@ -0,0 +1,98 @@ +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' + }) + ) + }) +}) diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 7b25f23dd..c8788352d 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -1152,6 +1152,7 @@ function baseCreateRenderer< const nextTree = renderComponentRoot(instance) const prevTree = instance.subTree instance.subTree = nextTree + next.el = vnode.el // beforeUpdate hook if (bu !== null) { invokeHooks(bu) @@ -1673,36 +1674,40 @@ function baseCreateRenderer< 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 - } + } else { + if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) { + vnode.suspense!.unmount(parentSuspense, doRemove) + return + } - if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) { - vnode.suspense!.unmount(parentSuspense, doRemove) - return - } + if (shouldInvokeDirs) { + invokeDirectiveHook(vnode, null, parentComponent, 'beforeUnmount') + } - if ((vnodeHook = props && props.onVnodeBeforeUnmount) != null) { - invokeVNodeHook(vnodeHook, parentComponent, vnode) - } - 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 (