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节点已经插入到页面中。
剩下的逻辑都很简单了,这里不再赘述。