浅析VUE双向绑定原理,实现数据监听并通知订阅者

版权声明:本文为博主原创文章,未经博主允许不得转载。 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

到此即完成了数据的监听和通知订阅功能。

猜你喜欢

转载自blog.csdn.net/u011350550/article/details/79119331
今日推荐