Vue
可以自定义局部和全局指令,具体用法参考官网即可,这篇文章讲述其内部实现。
parse
在上篇指令概述中已有大概说到,在processAttrs
函数中会处理自定义指令,这里详细说下。
1 | function processAttrs(el) { |
直接看代码有点抽象,我们用一个例子:
1 | <p v-mydir:myarg.foo.bar="msg">111</p> |
经过parseModifiers
处理后得到的modifiers
为
1 | { |
然后经过replace(modifierRE, '')
和replace(dirRE, '')
后,我们得到的name
就是mydir:myarg
了。其中myarg
是指令的参数,指令是可以接收参数的,参考官网教程。之后的argMatch
就是用来解析指令的参数,最终我们得到的name
就是mydir
,arg
是就是myarg
。
addDirective
向el.directives
塞入上述得到的几个属性值:
1 | export function addDirective(el: ASTElement, name: string, rawName: string, value: string, arg: ?string, modifiers: ?ASTModifiers) { |
所以最终我们的el.directives
为:
1 | [ |
generate
依然如上篇文章所提,在genData
中会首先调用genDirectives
处理自定义指令:
1 | function genDirectives(el: ASTElement, state: CodegenState): string | void { |
经过处理,我们拿到的字符串为:
1 | ` |
之后经过render
生成vnode
,这些都会放到vnode.data.directives
上。
patch
上篇概述里已提到_update
中会在不停阶段调用指令的不同选项:
1 | function _update(oldVnode, vnode) { |
normalizeDirectives
用于获取自定义指令的具体选项:
1 | function normalizeDirectives(dirs: ?Array<VNodeDirective>, vm: Component): { [key: string]: VNodeDirective } { |
返回的res
对象中存放着指令的定义,key
为指令名,value
为指令的选项对象,通常包含bind
、insert
、update
等钩子函数。resolveAsset
方法就是获取指令的定义,会将指令名转为驼峰、中划线的各种形式来尝试获取
拿到指令定义后,如果是新增的指令,则执行callHook(dir, 'bind', vnode, oldVnode)
调用指令的bind
方法:
1 | function callHook(dir, hook, vnode, oldVnode, isDestroy) { |
如果不是第一次绑定,则调用update
钩子函数。
之后如果vnode
是第一次创建,isCreate
为true
,会把dirsWithInsert
数组中的回调合并到 vnode.data.hook.insert
中。,如果不是则直接执行dirsWithInsert
中的回调。insert
钩子的执行时机应该是dom
节点已经插入到页面中。
剩下的逻辑都很简单了,这里不再赘述。