06-vue源码学习-理解依赖收集和派发更新(Observer、Watcher、Dep)

转载至:彻底理解Vue中的Watcher、Observer、Dep

在数据响应化时,在getter方法中做依赖收集,在setter方法中做派发更新。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(),
            ]
        }
    }
  }
}

我们来一步步实现看下。

obj对象上新增__ob__属性,值为Observe 类的实例,我们编写一个 def 函数,用来增加属性

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

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

Observe 实现

const Dep = require('./Dep')

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
  });
}


module.exports = Observer

这里面牵扯到了 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;

module.exports=Dep

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];
  }

  update() {
    //加入队列
  }
}

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的订阅者数组内

最后,改变数据时候,触发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)执行回调函数,整个流程结束

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值