阅读 7128

彻底理解Vue中的Watcher、Observer、Dep

思考以下代码

new Vue({
  el: '#example',
  data(){
      return{
          obj:{
              a:1
          }
      }
  },
})
复制代码

当我们写下这行代码时,vue将我们在data内定义的obj对象进行依赖追踪.

具体做法为执行new Observer(obj)

//经过上面的代码,我们的obj对象会变为以下的样子
{
  obj:{
    a:1,
    __ob__:{ //Observer 实例
        dep:{Dep 实例
            subs:[ //存放 Watcher 实例
              new Watcher(),
              new Watcher(),
              new Watcher(),
              new Watcher(),
            ]
        }
    }
  }
}
复制代码

我们来一步步实现看下。

  1. obj对象上新增__ob__属性,值为Observe 类的实例,我们编写一个 def 函数,用来增加属性
function def(obj, key, val, enumerable) {
  Object.defineProperty(obj, key, {
    value: val,
    enumerable: !!enumerable,
    writable: true,
    configurable: true
  });
}

复制代码

增加啥属性呢?之前提到了,我们需要增加一个 Observer 实例,实现如下

Observe 实现

class Observer {
  constructor(targetObject) {
    def(targetObject, '__ob__', this);//在 targetObject 上 添加  Observer 实例, setter时 通知该实例
    this.walk(targetObject)
    this.dep = new Dep()
  }

  walk(obj) {
    Object.keys(obj).forEach(key => {
      defineReactive(obj, key, obj[key])
    });
  }

}

function observe(data) {
  if (Object.prototype.toString.call(data) !== '[object Object]') {
    return
  }
  new Observer(data)
}

function defineReactive(obj, key, val) {
  observe(val)

  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter() {
      console.log('get');
      const ob = this.__ob__
      ob.dep.depend();
      return val
    },
    set: function reactiveSetter(newVal) {
      console.log('set');
      if (newVal === val) return
      val = newVal
      observe(newVal)
      const ob = this.__ob__
      ob.dep.notify();
    },

  })
}

function def(obj, key, val, enumerable) {
  Object.defineProperty(obj, key, {
    value: val,
    enumerable: !!enumerable,
    writable: true,
    configurable: true
  });
}
复制代码

这里面牵扯到了 Dep,我们也把Dep实现下,

Dep

class Dep {
  constructor() {
    this.subs = []
  }

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

  depend() {
    this.subs.push(Dep.target)
  }

  notify() {
    for (let i = 0; i < this.subs.length; i++) {
      this.subs[i].fn()
    }
  }
}

Dep.target = null


复制代码

Observer 类 主要做了以下事情

  1. 遍历 data 下的每一个属性,若是对象,则 执行 new Observer() ,在对象上新增__ob__属性,该属性的值为 Observer 的实例
  2. 劫持对象属性的变化,在 getter 的时候,拿到 Observer 实例的dep实例,执行dep.depend(),代码如下
  const ob = this.__ob__
  ob.dep.depend();
复制代码

看下 dep.depend()做了些啥

this.subs.push(Dep.target)
复制代码

Dep.target添加到 订阅数组内(this.subs)

也就是说,只要我们 Dep.target 赋值了,再执行 dep.depend(),那么该值就会被添加到 dep 的 subs 数组内,比如

Dep.target =function test(){}
dep.depend()
// test 函数就算 Dep 的订阅者了
复制代码

实现自动添加依赖

这个时候该 Watcher出场了

Watcher 实现

const Dep = require('./Dep')

class Watcher {
  constructor(vm, exp, fn) {
    this.vm = vm
    this.exp = exp
    this.fn = fn
    Dep.target = this//将自己挂载到 Dep.target,调用 Dep.depend时会读取该变量
    this.vm[exp]
  }
}

module.exports = Watcher
复制代码

根据一个小例子来理解 Watcher

const obj = {
  a: 1,
  b: {
    c: 2
  }
}

new Observer(obj)
new Watcher(obj, 'a', () => {
  console.log('Watcher 回调执行')
})
obj.a='222'

复制代码

流程如下:

  1. 先观测 obj 对象(new Observer(obj)
  2. 实例化Watcher时,会执行Dep.target = this,然后执行this.vm[exp],也就是取一次值,那么会触发 getter,将自身(Watcher实例)添加到dep的订阅者数组内
 get: function reactiveGetter() {
      const ob = this.__ob__
      ob.dep.depend();
      return val
    },
复制代码

最后,改变数据时候,触发setter

 set: function reactiveSetter(newVal) {
      if (newVal === val) return
      val = newVal
      observe(newVal)
      const ob = this.__ob__
      ob.dep.notify();
    },
复制代码

执行ob.dep.notify()

 notify() {
    for (let i = 0; i < this.subs.length; i++) {
      this.subs[i].fn()
    }
复制代码

遍历 订阅者(subs)执行回调函数,整个流程结束

源代码

文章分类
前端
文章标签