版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011350550/article/details/79119331
浅析VUE双向绑定原理,实现属性变化的监听
本文引用了“邓木琴居然被盗用了”的文章内容,博文地址: https://segmentfault.com/a/1190000006599500
一、VUE双向绑定原理简单介绍
Vue的双向绑定是通过数据劫持结合发布-订阅者模式实现的,即通过Object.defineProperty监听各个属性的setter,然后通知订阅者属性发生变化,触发响应的回调。原理图如下:
整个过程分为以下几步:
1、Observer通过Object.defineProperty实现对属性的变化监听,在变化是通知订阅者。
2、Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
2、Watcher是订阅者,是Observer和Compile的中间纽带,负责将变化的数据更新到视图,
二、属性变化的监听
1、监听属性变化(Object.defineProperty)直接上代码:
```
class Observer{
//构造器
constructor(data){
this.className = 'Observer';
this.observe(data);
}
//监听数据data的所有属性
observe(data) {
if(!this.isObj(data)){
return;
}
Object.keys(data).forEach((key)=>{
this.defineReative(data,key,data[key]);
})
}
//判断obj是否为对象,是返回true
isObj(obj) {
return !!data&&(typeof obj ==='object')
}
//监听data的key属性
defineReative(data,key,val){
this.observe(val);//如果子属性是对象的话,继续监听
Object.defineProperty(data,key,{
enumerable: true, // 可枚举
configurable: true, // 可配置
get: function() {
return val;
},
set: function(newVal) {
console.log('监听数据变化 ', val, ' --> ', newVal);
val = newVal;
}
})
}
}
```
简单解析下这段代码,代码使用了ES6的语法:
函数defineReactive()中是用Object.defineProperty对属性进行了重新定义,从而在setter函数中监听属性的变化。不熟悉Object.defineProperty的同学百度一下。^.^
测试一下,
```
let data = {
name:'xxx',
age:18
}
let observe = new Observer(data);
data.name = 'lalala';
```
打开控制台会输出:<br/>
监听数据变化 xxx --> lalala。<br/>
现在已经监控到数据发生变化,即在setter中通知订阅者属性发生即可。所以需要实现订阅者的管理,包括增删改查,通知订阅者属性变化。
```
//管理订阅者
class Dep{
constructor(){
//订阅者列表
this.subs = [];
//指向当前订阅者
this.target = null;
}
//添加订阅者
addSub(sub){
this.subs.push(sub);
}
//通知订阅者属性发生变化
notify(newVal) {
this.subs.forEach((sub) => {
sub.update(newVal);
});
}
//删改查等等操作暂时略
}
```
上面我们已经知道,可以在setter中通知订阅者发生变化,那在什么地方进行订阅呢,即需要使用属性的地方,当然就是在getter里订阅,所以defineReactive修改如下:
```
defineReative(data,key,val){
this.observe(val);//如果子属性是对象的话,继续监听
let dep = new Dep();
Object.defineProperty(data,key,{
enumerable: true, // 可枚举
configurable: true, // 可配置
get: function() {
if(Dep.target){
dep.addSub(Dep.target);
}
return val;
},
set: function(newVal) {
if(val !== newVal){
dep.notify(newVal);
val = newVal;
//console.log('监听数据变化 ', val, ' --> ', newVal);
}
}
})
}
```
Watcher如下:
```
class Watcher{
//监控data的key属性,cb是发生变化时的回调函数
constructor(data,key,cb){
this.data = data;
this.key = key;
this.cb = cb;
this.value = this.get();
}
update(newVal) {
this.run(newVal);
}
run(newVal) {
let oldVal = this.value;
if (newVal !== oldVal) {
this.cb(newVal, oldVal);
this.value = newVal;
}
}
get(){
Dep.target = this;
this.value = this.data[this.key];//触发getter里订阅
Dep.target = null;
return this.value;
}
}
```
测试一下:
let data = {
name:'xxx',
age:18
}
function monitorData(obj){
new Observer(obj);
new Watcher(obj,'name',function (newVal,oldVal) {
console.log('change,订阅者1:'+ oldVal + '-->' + newVal);
})
}
monitorData(data);
data.name = 'xbd';
输出为:change,订阅者1:xxx-->xbd
到此即完成了数据的监听和通知订阅功能。