var show = {
bind: function bind (el, ref, vnode) {
var value = ref.value;
//省略动画相关
{
el.style.display = value ? originalDisplay : 'none';
}
},
update: function update (el, ref, vnode) {
var value = ref.value;
var oldValue = ref.oldValue;
// 省略动画相关
{
el.style.display = value ? el.__vOriginalDisplay : 'none';
}
}
在前面的章节中我们其实多次提到了
directives
指令相关内容,不过都是分散在各个生命周期中的。因此大家对指令的整个工作原理还没有很明白,这一篇文章,让我们以内置指令v-model
为例,来看看一个指令的完整生命周期内的工作原理初始化
在前面《组件的初始化过程》一章中我们其实讲到过,内置指令其实在Vue类的创建过程中就被创建出来,并存储在
Vue.options.directives
中,作为默认存在的指令。代码如下:platforms/web/runtime/index.js
假设我们有如下组件:
当我们通过
new Vue(options)
创建实例的时候,会调用_init
进行初始化,在_init
函数内部,会把options
和Vue.options
进行合并,因此我们的组件$options
中就有了默认的directives
:core/instance/init.js
上面的代码
mergeOptions
会进行合并,最终vm.$options
包含了我们创建实例是传入的自定义指令和系统自带的v-model
指令。而,我们生成的
render
函数是这样的:在生成
render
函数的时候,通过对模板的语法解析,已经知道我们用到了v-model
这个指令,并解析出了他的参数。这样我们创建出来的vnode
就存在一个vnode.data.directives
保存了v-model
指令。 大家可能注意到了,这里面还生成了一个input
事件,在我们输入的时候会修改message
。那么我们的v-model
其实在render
函数中变成了两部分:v-model
指令的配置input
事件这样我们执行
render
之后,得到的vnode
的data
字段是这样的:输入框如何更新 message
在
patch
阶段,在createElm
函数中会通过invokeCreateHooks
来绑定input
事件。具体代码这里不贴出来,感兴趣的可以去 core/vdom/patch.js 中看invokeCreateHooks
相关的代码。在
patch
完成之后,最后一段代码是invokeInsertedHook
,会调用v-model
的inserted
方法,我们看看这个方法:platform/web/runtime/directives/model.js
可以发现
inserted
主要就是调用DOM的API添加了几个事件监听,主要是这三行代码:如果你以为这几个回调函数比如
onCompositionEnd
里面会去修改message
,那你就错了。其实这几个回调完全不是为了做数据修改,而是为了为了生成一个input
事件。没错,这几行代码只是为了处理兼容性,在各种浏览器环境中都能生成正确的input
事件。而当我们输入框的内容发生变化的时候,其实是由
input
事件触发的。input函数正是我们render函数生成的:如果
$event.target.composing
为true
,说明是在组合事件,因此不用管。等组合事件结束了,会触发inpunt
事件,此时只要去更新一下值就行了。这样我们就理解了我们输入的时候,是如何更新message
的值的。我画了一个图来表示 input 是如何更新
message
值的,其实就是这么简单:那么,当
message
被其他地方更新的时候,输入框的值是怎么更新的呢?message 被更新时,输入框如何一起更新
可能有人第一反应是
v-model
会监听this.message
的更新,然后通过input.value
来设置输入框的值。实际上这种想法是完全错误的,因为v-model
指令根本不用处理这种情况,这是交给patch
来做的。当message
被更新的时候, 会触发vm._update
来更新组件,最终会把这个更新patch到真实的DOM
上。所以,如果我们画一个流程图,其实是这样的:记住这一点:在
v-model
插件中,如果我们要更新DOM,只需要修改vm的状态,它自己会进行patch来更新。关于patch
的机制请参阅之前的文章。v-show
那么可能大家会想,是不是在
vue2.x
中由于vdom的存在,我们的指令就不会操作真实DOM呢?其实并不是,这里简单看下内置指令v-show
的代码:platform/web/runtime/directives/show.js
主要的代码都是对动画的处理,这里我们不考虑动画部分,那么其实在
v-show
指令中,真的是对原生DOM进行操作的:我们写指令的时候,在生命周期的阶段都可以直接访问原生DOM,而且指令本来作用就是
拓展
DOM 能里的,因此指令中操作原生DOM是很常见的操作。因此我们也可以知道,这些需要操作原生DOM的指令,和平台是相关的,他们的代码在platforms
里而不是core
里面。Vue 官方对指令生命周期的定义如下:
一个指令定义对象可以提供如下几个钩子函数 (均为可选):
在生命周期的各个阶段,第一个参数都是
el
.