Vue的全局API: directive、filter和component注册以下:node
ASSET_TYPES.forEach(function (type) {
Vue[type] = function (
id,
definition
) {
if (!definition) {// 过滤器注册
return this.options[type + 's'][id]
} else {
/* istanbul ignore if */
if (type === 'component') {
validateComponentName(id);
}
// 若是是全局注册组件,经过Vue.extend生成子组件构造函数
if (type === 'component' && isPlainObject(definition)) {
definition.name = definition.name || id;
definition = this.options._base.extend(definition);
}
// 注册或获取全局指令,此时并未失效,若是definition是函数则默认监听bind和update事件
if (type === 'directive' && typeof definition === 'function') {
definition = { bind: definition, update: definition };
}
this.options[type + 's'][id] = definition;
return definition
}
};
});
}
复制代码
以模板为例:express
<div id="app"><input type="text" class="form-control" v-model="keywords" v-focus></div>
复制代码
在模板编译阶段,处理属性的过程当中,会把v-model和v-focus这种解析成:bash
[
{arg: null, end: 72, isDynamicArg: false, modifiers: undefined, name: "model",rawName: "v-model",start: 54, value: "keywords"},
{arg: null,end: 83,isDynamicArg: false,modifiers: undefined,name: "focus",rawName: "v-focus",start: 73, value: ""}
]
复制代码
存在el.directives属性中,生成的渲染函数以下:app
with(this){return _c('div',{
attrs:{"id":"app"}
},[
_c('input',{
directives:
[
{
name:"model",
rawName:"v-model",
value:(keywords),
expression:"keywords"
},
{
name:"focus",
rawName:"v-focus"
}
],
staticClass:"form-control",
attrs:{"type":"text"},
domProps:{"value":(keywords)},
on:{"input":function($event){
if($event.target.composing)
return;
keywords=$event.target.value
}
}
})
])}
复制代码
input生成的Vnode以下:dom
{
asyncFactory: undefined,
asyncMeta: undefined,
children: undefined,
componentInstance: undefined,
componentOptions: undefined,
context: Vue {_uid: 0, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: Vue, …},
data: {directives: Array(2), staticClass: "form-control", attrs: {type: "text"}, domProps: {value: undefined}, on: {input: f}},
elm: undefined,
fnContext: undefined,
fnOptions: undefined,
fnScopeId: undefined,
isAsyncPlaceholder: false,
isCloned: false,
isComment: false,
isOnce: false,
isRootInsert: true,
isStatic: false,
key: undefined,
ns: undefined,
parent: undefined,
raw: false,
tag: "input",
text: undefined,
child: undefined
}
复制代码
在渲染阶段,patchVnode过程当中,会执行async
for (i = 0; i < cbs.update.length; ++i) { cbs.update[i](oldVnode, vnode); }
复制代码
更新属性的函数为函数
[
ƒ updateAttrs(oldVnode, vnode),
ƒ updateClass(oldVnode, vnode),
ƒ updateDOMListeners(oldVnode, vnode),
ƒ updateDOMProps(oldVnode, vnode),
ƒ updateStyle(oldVnode, vnode),
ƒ update(oldVnode, vnode),
ƒ updateDirectives(oldVnode, vnode)
]
复制代码
其中指令相关的处理逻辑post
function updateDirectives (oldVnode, vnode) {
if (oldVnode.data.directives || vnode.data.directives) {
_update(oldVnode, vnode);
}
}
复制代码
_update函数为:ui
function _update (oldVnode, vnode) {
var isCreate = oldVnode === emptyNode;
var isDestroy = vnode === emptyNode;
var oldDirs = normalizeDirectives$1(oldVnode.data.directives, oldVnode.context);
var newDirs = normalizeDirectives$1(vnode.data.directives, vnode.context);
var dirsWithInsert = [];
var dirsWithPostpatch = [];
var key, oldDir, dir;
for (key in newDirs) {
oldDir = oldDirs[key];
dir = newDirs[key];
if (!oldDir) {
// new directive, bind
// 新的指令触发bind方法
callHook$1(dir, 'bind', vnode, oldVnode);
if (dir.def && dir.def.inserted) {
dirsWithInsert.push(dir);
}
} else {
// existing directive, update
// 指令存在,触发update
dir.oldValue = oldDir.value;
dir.oldArg = oldDir.arg;
callHook$1(dir, 'update', vnode, oldVnode);
// 判断是否设置componentUpdated方法,有则将其添加到列表中,确保组件的VNode和其子VNode所有更新后再调用指令的componentupdated方法
if (dir.def && dir.def.componentUpdated) {
dirsWithPostpatch.push(dir);
}
}
}
// 指令添加到dirsWithInsert,保证全部的bind执行完毕再执行insert函数
if (dirsWithInsert.length) {
var callInsert = function () {
for (var i = 0; i < dirsWithInsert.length; i++) {
callHook$1(dirsWithInsert[i], 'inserted', vnode, oldVnode);
}
};
// 判断虚拟节点是不是新建立节点
if (isCreate) {
// 能够将一个钩子函数与虚拟节点现有的钩子函数合并在一块儿,当虚拟节点触发钩子函数时,新增的钩子也会触发,这里应该等到元素被插入到父节点后再执行指令的insert
mergeVNodeHook(vnode, 'insert', callInsert);
} else {
// 不须要延迟到绑定元素插入到父节点后进行,直接执行callInsert
callInsert();
}
}
if (dirsWithPostpatch.length) {
// 触发update钩子后,再触发postpatch
mergeVNodeHook(vnode, 'postpatch', function () {
for (var i = 0; i < dirsWithPostpatch.length; i++) {
// componentUpdated指令推迟到vnode和oldVnode更新后
callHook$1(dirsWithPostpatch[i], 'componentUpdated', vnode, oldVnode);
}
});
}
if (!isCreate) {
// 新建立的虚拟节点不须要解绑,若是newDirs中不存在,则触发unbind方法
for (key in oldDirs) {
if (!newDirs[key]) {
// no longer present, unbind
callHook$1(oldDirs[key], 'unbind', oldVnode, oldVnode, isDestroy);
}
}
}
}
复制代码
这里通过normalizeDirectives函数处理后变成:this
[
{
v-focus: {
def: {bind: ƒ, inserted: ƒ, updated: ƒ},
modifiers: {},
name: "focus",
oldArg: undefined,
oldValue: undefined,
rawName: "v-focus"
}
},
v-model:{
def: {inserted: ƒ, componentUpdated: ƒ},
expression: "keywords",
modifiers: {},
name: "model",
oldArg: undefined,
oldValue: "wewe",
rawName: "v-model",
value: "wewew"
}
]
复制代码
callHook如何执行的钩子函数以下:
function callHook$1 (dir, hook, vnode, oldVnode, isDestroy) {
var fn = dir.def && dir.def[hook];
if (fn) {
try {
fn(vnode.elm, dir, vnode, oldVnode, isDestroy);
} catch (e) {
handleError(e, vnode.context, ("directive " + (dir.name) + " " + hook + " hook"));
}
}
}
复制代码
更新完directive后继续进行diff,最后挂载到页面。