Vue 的响应式原理

1. 前言

  • data中的数据发生改变,Vue内部是如何监听message数据的改变?

    使用Object.defineProperty -----> 监听对象属性的改变

  • 当数据发生改变,Vue是如何知道要通知哪些人?哪些页面进行更新?

    使用 发布订阅者模式 实现

2. 过程

image-20211028132925800

2.1 首先new Vue()

new Vue({
  el: "#app",
  data: {
    name: "fct",
    age: 18
  }
})

2.2 针对data中的数据

利用Object.defineProperty对data对象中的数据进行重构,设置getset。用以监听数据的改变。

new Observer(data);	// 处理data

// 观察者
class Observer {	
  constructor(data) {	// data 即为 Vue中data对象数据
    this.data = data;
    Object.keys(data).forEach(item => {
      this.defineReactive(this.data, key, data[key])
    })
  }
  defineReactive(obj, key, val) {
    // 一个属性 key ,对应一个 Dep 对象
    const dep = new Dep();
    Object.defineProperty(obj, key, {
      enumerable: true,
      configurable: true,
      set(newValue) {
        if (newValue === value) {
          return;
        }
        value = newValue;
        // 通知订阅者对象watcher,进行视图更新
        dep.notify();
      },
      get() {
        // 添加到订阅者队列
        if (Dep.target) {
          // target 即为watcher
          dep.addSub(Dep.target);
        }
        return value;
      }
    })
  }
}

  • 每个data中的属性都对应一个Dep对象(dependence依赖),其中有属性:subs(订阅者subscriber),该属性对应的值是数组,数组中每一项为watcher对象(观察者)。

    class Dep {
      constructor() {
        this.subs = []	// 存放watcher对象数组
      }
      notify() {	// 通知subs对象中所有watcher,进行更新
        // watcher 对象都有update方法进行视图更新
        this.subs.forEach(sub => {
          sub.update();
        })
      }
      addSub(sub) {	// 添加 watcher对象
        this.subs.push(sub);
      }
    }
    

2.3 针对 el 模板

解析模板中何处使用了data中的数据,为每一处使用创建一个watcher对象。并将对象推入到相应data数据的Dep对象的subs数组中。

new Compiler('el', this);	//处理el('#app')

class Compiler {	//编译
  constructor() {
    // ....
    new Watcher();	//编译时找到{{}}使用了data中的属性,新建watcher对象
  }
}

class Watcher {
  constructor() {
    Dep.target = this;	//1. 设置target,getter中if满足条件
    this.update();		//2. 进订阅者数组
    Dep.target = null;	//3. 赋空
  }
  update() {
    // 更新视图, this.node.nodeValue设置值
    this.node.nodeValue = this.vm[this.name];	// this.vm即为Vue实例
    // this.vm[this.name]取属性值,会调用data对象属性的getter,然后添加进sub数组
  }
}

3. 总结

image-20211028145627272

posted @ 2021-10-28 14:58  青柠i  阅读(38)  评论(0编辑  收藏  举报