Vue响应式原理(二)Observer、Dep、Watcher

1,430 阅读2分钟

前言


结合上文发布订阅模式,理解Vue的响应式设计

Object.defineProperty


Vue官网经典图

看着感觉就是观察者模式,Data变动时就通知观察者更新,不就是利用Object.defineProperty来改造数据的读写,在它被改变的时候通知所有watcher从而触发视图更新吗?Dep在哪呢?

源码中的Observer、Dep和Watcher


tips:这里贴的代码是我看完后,自己简化的,类名和变量名都是源码中的

Vue封装了一个defineReactive方法来对数据进行defineProperty改造

function defineReactive (
  obj: Object,
  key: string,
  val: any,
) {
    Object.defineProperty(obj, key, {
    
        get: function reactiveGetter () {
            //添加依赖
            dep.depend()

            return value
        },
        
        set: function reactiveSetter (newVal) {
            //发布
            dep.notify()
        }
    })
}

这里能看出这个defineReactive方法在改造数据的时候,数据get时进行依赖的添加,set时发布,可以看出,dep类似消息中介

class Observer {
  value: any;
  dep: Dep;
  
  constructor (value: any) {
    this.value = value
    this.dep = new Dep()
    if(Array.isArray(value)){
      //遍历出来挨个用Observer去new一下
    }else{
       //是对象就遍历,挨个进行响应式改造改造
       const keys = Object.keys(obj)
       for (let i = 0; i < keys.length; i++) {
           defineReactive(obj, keys[i])
       }
    }
 }

Observer的作用其实就是改造数据,而Vue让每一个响应式的数据都是被Observer改造过的,那么是不是可以理解为,每一个被Observer new 过的数据就是类似publisher发布者的存在

再看一下Dep类

class Dep {
  static target: ?Watcher;
  id: number;
  subs: Array<Watcher>;

  constructor () {
    this.id = uid++
    this.subs = []
  }

  addSub (sub: Watcher) {
    this.subs.push(sub)
  }

  removeSub (sub: Watcher) {
    remove(this.subs, sub)
  }

  depend () {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
  }

  notify () {
    const subs = this.subs.slice()
 
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
}

dep里维护一个watcher列表,并且有dependnotify方法,能添加watcher并且发布通知,印证了这个dep就是消息中介

Watcher类,addDep添加依赖(订阅),并具有update方法

class Watcher {
  addDep (dep: Dep) {
    const id = dep.id
    //添加依赖(像是订阅操作i)
    dep.addSub(this)
  }

  update () {
      this.run()
  }

  run () {
    if (this.active) {
       //执行数据更新
    }
  }

总结


每一个数据都会被Observer改造成响应式的,这个响应式的数据就是发布者,每一个数据对应有一个自己的dep

读数据时,触发get,调用dep的depend方法添加所有watcher(订阅,是调用了watcher的addDep方法)

改数据时,触发set,调用dep的notify方法通知所有watcher(订阅者)执行更新。

以上是vue2的原理,vue3待写


参考