vue之响应式原理

响应式,顾名思义,一个地方改变,触发另一个地方改变,在vue的体现就是,data的某个属性值改变,自动触发了视图更新

说到响应式,可以谈谈观察者模式

观察者模式是一种实现一对多关系解耦的行为设计模式。它主要涉及两个角色:观察目标、观察者

class Watcher {
    constructor(name) {
        this.name = name;
    }
    receiveMessage(message) {
        console.log(`${this.name}收到消息:${message}`);
    }
}
class Phone {
    constructor() {
        this.watchers = [];
    }
    addWatcher(watcher) {
        this.watchers.push(watcher)
        return this;
    }
    notify(message) {
        this.watchers.forEach(watcher => {
            watcher.receiveMessage(message);
        })
    }
}

let phone = new Phone();
let watcher1 = new Watcher('监听者1');
let watcher2 = new Watcher('监听者2');
phone.addWatcher(watcher1).addWatcher(watcher2);
phone.notify('开饭了');

上面的例子中,Watcher是观察者,Phone是被观察目标。可以看出,想要实现一个观察者模式有2个要点 

  1. 维护一个观察者队列
  2. 通知消息

vue响应式原理跟上面的思想是一样的,最大的区别是,上面我们显式地调用了addWatcher来添加监听者、调用了notify来触发更新,而开发中只需要改变data某个属性值就可以触发视图更新,很明显,这些事情都是vue内部帮我们做了。

通常我们把添加监听者,叫做依赖收集。

Object.defineProperty 相信大家都不陌生了,显然,vue要做的就是遍历data里的数据 ,通过defineProperty,在 get 里收集依赖,在 set 派发更新。

这是删减版的defineReactive函数

  function defineReactive$$1(
    obj,
    key,
    val,
    customSetter,
    shallow
  ) {
    var dep = new Dep(); //{subs:[renderWatcher,computedWatcher1,computedWatcher2]}
    Object.defineProperty(obj, key, {
      enumerable: true,
      configurable: true,
      get: function reactiveGetter() {
        var value = getter ? getter.call(obj) : val;
        if (Dep.target) { 
          dep.depend();// 依赖收集
        }
        return value
      },
      set: function reactiveSetter(newVal) {
        val = newVal;
        dep.notify(); //通知更新
      }
    });
  }
  • 函数里有一个dep对象,用于辅助管理观察者队列,它有一个subs属性,subs里的项是watcher
  • dep.depend就是把当前的watcher对象收集到subs里,对应观察者模式的addWatcher
  • dep.notify就是执行subs里的每一个watcher对象里的更新方法,对应观察者模式的派发更新

关于Watcher类,有三类,为renderWatcher 、computedWatcher、userCustomWatcher。

  • renderWatcher就是更新时会触发render的watcher
  • computedWatcher就是遍历computed对象,生成的watcher
  • userCustomWatche就是r遍历watch对象,生成的watcher

何时触发 get 函数?

对于computedWatcher、userCustomWatcher,很明显,执行我们定义的函数,就会访问到我们书写的变量,从而触发 get ;

而renderWatcher可能容易弄混,因为我们一般不直接书写render函数,而是写template,我一开始就以为编译template时就会触发get。。。但其实这是两个完全独立的过程,编译可在运行时,也可预编译,最终都会把template转成render函数(准确说时render函数字符串)。

这里有个调试小技巧,在get里打一个debugger

这样你就能在右侧面板的Call Stack清楚的看到谁触发了它,过程中发生了什么,而不会看着看着就不知道被绕进哪了,迷失在茫茫代码中。

现在我们大概可以总结vue响应式原理

  • 遍历data,拦截对象属性的get和set方法 (数组是劫持数组原型方法)
  • 对于每一个属性,闭包里有一个dep对象,它有subs数组,每一项都是一个watcher
  • 访问data的某个属性会触发get,这里进行依赖收集,就是往subs里添加watcher
  • 修改data的某个属性会触发set,这里进行派发更新,就是执行subs里所有watcher的更新方法

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值