Vue2数据双向绑定的原理
双向绑定在于数据变化更新视图,视图变化更新数据,因为视图更新数据可以通过事件监听来控制,着重点就是数据变化如何更新视图。
·
一、Vue的双向绑定
双向绑定在于数据变化更新视图,视图变化更新数据,因为视图更新数据可以通过事件监听来控制,着重点就是数据变化如何更新视图。
二、实现过程
监听器observer对数据进行劫持监听,数据对象都会通过Object.defineProperty设置set和get方法。由于data中的某个key在一个视图中可能出现多次,所以订阅者是有多个的,所以需要一个消息订阅器dep来专门收集这些订阅者。当订阅者watcher接收到相应属性的变化(如果属性发生变化,会触发set方法),就会执行对应订阅者的更新函数,从而更新视图。
三、代码实现
1、实现一个Observer,通过递归遍历所有属性,通过Object.defineProperty给所有属性添加set和get方法。
//给对象的所有属性添加set、get方法
function defineReactive(data, key, val) {
//递归遍历子属性
observe(val)
Object.defineProperty(data, key, {
//可枚举
enumerable: true,
//可以被配置
configurable: true,
get: function() {
return val;
},
set: function(newval) {
val = newval;
console.log("数据发生了变化");
}
})
}
function observe(data) {
//如果不是对象直接返回
if (!data || typeof data != "object") {
return;
}
Object.keys(data).forEach(function(key) {
defineReactive(data, key, data[key])
})
}
2、创建一个可以容纳订阅者的消息订阅器Dep,订阅器Dep主要负责收集订阅者,然后在属性变化的时候执行对应订阅者的更新函数。
//2、给对象的所有属性添加set、get方法
function defineReactive(data, key, val) {
//递归遍历子属性
observe(val);
var dep = new Dep();
Object.defineProperty(data, key, {
//可枚举
enumerable: true,
//可以被配置
configurable: true,
get: function() {
if (是否需要订阅者) {
dep.addSub(watcher); //添加一个订阅者
}
return val;
},
set: function(newval) {
if (newval === val) {
return;
}
val = newval;
console.log("数据发生了变化");
//数据发生变化,通知所有订阅者
dep.notify();
}
})
}
//1、创建数据监听器
function observe(data) {
//如果不是对象直接返回
if (!data || typeof data != "object") {
return;
}
Object.keys(data).forEach(function(key) {
defineReactive(data, key, data[key])
})
}
//3、创建订阅器Dep
function Dep() {
this.subs = [];
}
//4、给Dep增加 添加订阅者和通着订阅者更新的方法
Dep.prototype = {
addSub: function(sub) {
this.subs.push(sub);
},
notify: function() {
this.subs.forEach((sub) => {
sub.update();
})
}
}
3、实现一个watcher,可以收到属性的变化通知执行相应的函数
//5、创建订阅者watcher
function watcher(vm, exp, cb) {
this.cb = cb;
this.vm = vm;
this.exp = exp;
this.value = this.get(); //触发get方法,将自己添加到订阅器
}
watcher.prototype = {
update: function() {
this.run();
},
run: function() {
//获取新值
var value = this.vm.data[this.exp];
//获取当前值
var oldVal = this.value;
if (value != oldVal) {
this.value = value;
this.cb.call(this.vm, value, oldVal)
}
},
get: function() {
Dep.target = this; //缓存自己
var value = this.vm.data[this.exp]; //强制执行监听器里的get函数
Dep.target = null; //释放自己
return value;
}
}
4、稍微修改Observer的defineReactive方法
5、将Observer和watcher关联起来
//6、将Observer和Watcher关联起来
function SelfVue(data, el, exp) {
this.data = data;
observe(data);
el.innerHTML = this.data[exp]; //初始化模板数据的值
new Watcher(this, exp, function(value) {
el.innerHTML = value;
})
return this;
}
更多推荐
已为社区贡献2条内容
所有评论(0)