Vue的构造函数分析
vm就是MVVM中的View Model
var vm = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
/*
相关于Vue的构造函数
*/
function Vue(options) {
// 将选项对象保存到vm
this.$options = options;
// 将data对象保存到vm和data变量中
var data = this._data = this.$options.data;
//将vm保存在me变量中
var me = this;
// 遍历data中所有属性
Object.keys(data).forEach(function (key) { // 属性名: name
// 对指定属性实现代理: 通过Object.defineProperty(obj,attr,{配置项, get:func, set:func})方法,将data中遍历的属性添加到vm实例中实现代理,使得原本 vm._data[attr]访问方式变为vm.attr
me._proxy(key);
});
// 对data进行监视,在observe中创建与当前属性对应的dep对象,当调用data中的属性值时,建立dep与watcher的关系;当更新data中的值时,通过dep.notify()方法通知当前dep对应的所有的watcher对界面数据进行更新
observe(data, this);
// 创建一个用来编译模板的compile对象
this.$compile = new Compile(options.el || document.body, this)
}
- 数据绑定
- 初始化显示: 页面(表达式/指令)能从data读取数据显示 (编译/解析)
- 更新显示: 更新data中的属性数据==>页面更新
Dep和Watcher对象
Dep
与data中的属性一一对应
{ id: 0, subs[对应的Watcher实例对象数组] }
Watcher
与模板中一般指令(v-html、v-model、v-class...)/大括号表达式({{ }})一一对应
{
cb: 当对应的属性值发生了变化时, 自动调用该回调函数, 更新对应的节点,
vm: Vue实例对象,
exp: data属性表达式,例如:a.b.c,
depIds: 用于存储对应的dep对象 {0: d0, 1: d1, 2: d2},
value: 当前watcher中的属性值
}
- 什么时候一个dep中关联多个watcher?
多个指令或表达式用到了当前同一个属性 {{name}} {{name}} - 什么时候一个watcher中关联多个dep?
多层表达式的watcher对应多个dep {{a.b.c}}
整体过程
Vue是通过数据劫持的方式来做数据绑定的,最核心的方法是通过Object.defineProperty()
方法来实现对属性的劫持,达到监听数据变动的目的。以MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据model变更的双向绑定效果。
原理图如下:
Vue实例化一个对象的过程如下:
- mvvm入口函数,整合以下三者,创建一个实例后,遍历data中的所有属性实现数据代理,实现 vm.xxx 代替 vm._data.xxx
- 实现一个数据监听器Observer,利用Obeject.defineProperty()来监听数据对象的所有属性变动,并对应所有属性一一创建相应的dep对象,给dep对象添加订阅者(watcher);如Observer监测到有变动,set函数调用,触发Dep的notify()向对应的订阅者Watcher通知变化,Watcher调用update方法更新界面。
- 实现一个指令解析器Compile,主要做的事情是解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图,由于遍历解析的过程有多次操作dom节点,为提高性能和效率,会先将跟节点el转换成文档碎片fragment进行解析编译操作,解析完成,再将fragment添加回原来的真实dom节点中
- 实现一个Watcher
1、在自身实例化时往属性订阅器(dep)里面添加自己
2、自身必须有一个update()方法
3、待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调。
文章学习重要来源:https://segmentfault.com/a/1190000006599500