vue双向绑定原理
vue的双向绑定是由数据劫持结合发布者-订阅者模式实现。
什么是数据劫持?: vue通过Object.defineProperty()来劫持对象属性的setter和getter操作,在数据变动时做你想要做的事情。
var Book = {
};
Object.defineProperty(Book,'name',{
set:function(value) {
name = value;
console.log('你取了名叫:'+value);
},
get:function() {
console.log('get方法被监听到');
return '<'+name+'>';
}
});
Book.name = '张三'; //你取了名叫: 张三
console.log(Book.name); //<张三>
实现过程
我们已经知道如何实现数据的双向绑定了,那么首先要对数据进行劫持监听,所以我们首先要设置一个监听器Observer,用来监听所有的属性,当属性变化时,就需要通知订阅者Watcher,看是否需要更新。因为属性可能是多个,所以会有多个订阅者,故我们需要一个消息订阅器Dep来专门收集这些订阅者,并在监听器Observer和订阅者Watcher之间进行统一的管理。因为在节点元素上可能存在一些指令,所以我们还需要有一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令初始化成一个订阅者Watcher,并替换模板数据并绑定相应的函数,这时候当订阅者Watcher接受到相应属性的变化,就会执行相对应的更新函数,从而更新视图。
整理上面的思路,我们需要实现几个步骤,来完成双向绑定:
- Observer 监听器,用来劫持并监听所有属性,如果有变动的,就通知订阅者。
- Dep 存储依赖和派发更新,监听器和订阅者的桥梁。
- Watcher 订阅者,可以收到属性的变化通知并执行相应的函数,从而更新视图。
- Compile 解析器,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器
1、实现一个监听器
- 数据监听器的核心方法就是Object.defineProperty( ),通过遍历循环对所有属性值进行监听劫持监听
- 在属性的 get里面判断是否需要需要添加订阅者,这是为了让Watcher在初始化时触发
- 在属性的 set方法中,如果函数变化,就会通知对应的所有订阅者,订阅者们将会执行相对应的更新函数
function Observer(data) {
this.data = data;
this.walk(data);
}
Observer.prototype = {
walk: function(data) {
var self = this;
Object.keys(data).forEach(function(key) {
self.defineReactive(data, key, data[key]);
});
},
defineReactive: function(data, key, val) {
var dep = new Dep();
var childObj = observe(val);
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get: function getter () {
//Dep类在下一个文件详细说
//Watcher初始化触发、判断是否需要添加订阅者明
if (Dep.target) {
dep.addSub(Dep.target); //添加订阅者
}
return val;
},
set: function setter (newVal) {
if (newVal === val) {
return;
}
val = newVal;
dep.notify(