fix(runtime-core): rework vnode hooks handling

- peroperly support directive on components (e.g. <foo v-show="x">)
- consistently invoke raw vnode hooks on component vnodes (fix #684)
This commit is contained in:
Evan You
2020-02-10 13:15:36 -05:00
parent 8a87074df0
commit cfadb98011
4 changed files with 204 additions and 26 deletions

View File

@@ -98,6 +98,15 @@ describe('directives', () => {
expect(prevVNode).toBe(null)
}) as DirectiveHook)
const dir = {
beforeMount,
mounted,
beforeUpdate,
updated,
beforeUnmount,
unmounted
}
let _instance: ComponentInternalInstance | null = null
let _vnode: VNode | null = null
let _prevVnode: VNode | null = null
@@ -109,14 +118,7 @@ describe('directives', () => {
_prevVnode = _vnode
_vnode = withDirectives(h('div', count.value), [
[
{
beforeMount,
mounted,
beforeUpdate,
updated,
beforeUnmount,
unmounted
},
dir,
// value
count.value,
// argument
@@ -132,17 +134,17 @@ describe('directives', () => {
const root = nodeOps.createElement('div')
render(h(Comp), root)
expect(beforeMount).toHaveBeenCalled()
expect(mounted).toHaveBeenCalled()
expect(beforeMount).toHaveBeenCalledTimes(1)
expect(mounted).toHaveBeenCalledTimes(1)
count.value++
await nextTick()
expect(beforeUpdate).toHaveBeenCalled()
expect(updated).toHaveBeenCalled()
expect(beforeUpdate).toHaveBeenCalledTimes(1)
expect(updated).toHaveBeenCalledTimes(1)
render(null, root)
expect(beforeUnmount).toHaveBeenCalled()
expect(unmounted).toHaveBeenCalled()
expect(beforeUnmount).toHaveBeenCalledTimes(1)
expect(unmounted).toHaveBeenCalledTimes(1)
})
it('should work with a function directive', async () => {
@@ -198,4 +200,144 @@ describe('directives', () => {
await nextTick()
expect(fn).toHaveBeenCalledTimes(2)
})
it('should work on component vnode', async () => {
const count = ref(0)
function assertBindings(binding: DirectiveBinding) {
expect(binding.value).toBe(count.value)
expect(binding.arg).toBe('foo')
expect(binding.instance).toBe(_instance && _instance.proxy)
expect(binding.modifiers && binding.modifiers.ok).toBe(true)
}
const beforeMount = jest.fn(((el, binding, vnode, prevVNode) => {
expect(el.tag).toBe('div')
// should not be inserted yet
expect(el.parentNode).toBe(null)
expect(root.children.length).toBe(0)
assertBindings(binding)
expect(vnode.type).toBe(_vnode!.type)
expect(prevVNode).toBe(null)
}) as DirectiveHook)
const mounted = jest.fn(((el, binding, vnode, prevVNode) => {
expect(el.tag).toBe('div')
// should be inserted now
expect(el.parentNode).toBe(root)
expect(root.children[0]).toBe(el)
assertBindings(binding)
expect(vnode.type).toBe(_vnode!.type)
expect(prevVNode).toBe(null)
}) as DirectiveHook)
const beforeUpdate = jest.fn(((el, binding, vnode, prevVNode) => {
expect(el.tag).toBe('div')
expect(el.parentNode).toBe(root)
expect(root.children[0]).toBe(el)
// node should not have been updated yet
// expect(el.children[0].text).toBe(`${count.value - 1}`)
assertBindings(binding)
expect(vnode.type).toBe(_vnode!.type)
expect(prevVNode!.type).toBe(_prevVnode!.type)
}) as DirectiveHook)
const updated = jest.fn(((el, binding, vnode, prevVNode) => {
expect(el.tag).toBe('div')
expect(el.parentNode).toBe(root)
expect(root.children[0]).toBe(el)
// node should have been updated
expect(el.children[0].text).toBe(`${count.value}`)
assertBindings(binding)
expect(vnode.type).toBe(_vnode!.type)
expect(prevVNode!.type).toBe(_prevVnode!.type)
}) as DirectiveHook)
const beforeUnmount = jest.fn(((el, binding, vnode, prevVNode) => {
expect(el.tag).toBe('div')
// should be removed now
expect(el.parentNode).toBe(root)
expect(root.children[0]).toBe(el)
assertBindings(binding)
expect(vnode.type).toBe(_vnode!.type)
expect(prevVNode).toBe(null)
}) as DirectiveHook)
const unmounted = jest.fn(((el, binding, vnode, prevVNode) => {
expect(el.tag).toBe('div')
// should have been removed
expect(el.parentNode).toBe(null)
expect(root.children.length).toBe(0)
assertBindings(binding)
expect(vnode.type).toBe(_vnode!.type)
expect(prevVNode).toBe(null)
}) as DirectiveHook)
const dir = {
beforeMount,
mounted,
beforeUpdate,
updated,
beforeUnmount,
unmounted
}
let _instance: ComponentInternalInstance | null = null
let _vnode: VNode | null = null
let _prevVnode: VNode | null = null
const Child = (props: { count: number }) => {
_prevVnode = _vnode
_vnode = h('div', props.count)
return _vnode
}
const Comp = {
setup() {
_instance = currentInstance
},
render() {
return withDirectives(h(Child, { count: count.value }), [
[
dir,
// value
count.value,
// argument
'foo',
// modifiers
{ ok: true }
]
])
}
}
const root = nodeOps.createElement('div')
render(h(Comp), root)
expect(beforeMount).toHaveBeenCalledTimes(1)
expect(mounted).toHaveBeenCalledTimes(1)
count.value++
await nextTick()
expect(beforeUpdate).toHaveBeenCalledTimes(1)
expect(updated).toHaveBeenCalledTimes(1)
render(null, root)
expect(beforeUnmount).toHaveBeenCalledTimes(1)
expect(unmounted).toHaveBeenCalledTimes(1)
})
})