vue.js是采用数据劫持结合发布者-订阅模式的方式,通过Object.defineProperty()
来劫持各个属性的setter
,getter
,在数据变动时发布消息给订阅者,触发相应的监听回调来渲染视图。
具体实现的步骤:
第一步: 需要Observer
的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter
和getter
这样的话,给这个对象的某个值赋值,就会触发setter
,那么就能监听到了数据变化
第二步: compile
解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
第三步: Watcher
订阅者是Observer
和Compile
之间通信的桥梁,主要做的事情是:
1、在自身实例化时往属性订阅器(dep)里面添加自己
2、自身必须有一个update()
方法
3、待属性变动dep.notice()
通知时,能调用自身的update()方法,并触发Compile
中绑定的回调,则功成身退。
第四步: MVVM
作为数据绑定的入口,整合Observer
、Compile
和Watcher三者,通过Observer
来监听自己的model
数据变化,通过Compile
来解析编译模板指令,最终利用Watcher搭起Observer
和Compile
之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input)
-> 数据model
变更的双向绑定效果。
Object.defineProperty()
的理解
作用
Object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
参数
Object.defineProperty(obj, prop, descriptor)
obj: 必需 要定义属性的对象。
prop: 必需 要定义或修改的属性的名称。
descriptor: 必需 要定义或修改的属性描述符。(这个参数是一个对象)
var obj = { b: "123" }
console.log(obj)
Object.defineProperty(obj, "a", {
value: 37,//值
writable: true, //是否可以被修改
enumerable: true, //是否可以被枚举也就是是否可以遍历
configurable: true //是否可以被删除
});
// 控制台输出
for (let i in Object.keys(obj)) {
console.log(i)
}
obj.a = "36"
console.log(obj)
writable
enumerable
configurable
默认值都是false
,value
不给值的情况下是undefined
。
var Book = {}
var name = '';
Object.defineProperty(Book, 'name', {
set: function (value) { //value指当前给赋的值
name = value;
console.log('你取了一个书名叫做' + value);
},
get: function () {
return '《' + name + '》'
}
})
Book.name = 'vue'; // 你取了一个书名叫做vue权威指南
console.log(Book.name); // 《vue权威指南》
vue对象创建的时候会随机创建一个监听对象,监听对象负责监听属性的set方法有没有被调用,如果属性的set方法被调用,说明这个属性就要发生变化,我们的监听者,会调用这个属性的get方法,拿到最新的值然后通知观察者,观察者和指令解析器绑定,指令解析器是将每一个节点都绑定在一条指令上,指令触发节点发生更新,指令解析器有很多指令,每一个指令绑定一个dom节点,当监听者发生变化,拿到最新的值,通知观察者,观察者拿到最新的值,触发指令解析器,执行指令绑定的回调函数,然后更新视图,这就是双向数据绑定的原理。