Vue源码进程——响应式原理(二)

143 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第6天,点击查看活动详情

首先我们需要知道,实现一个响应式主要依靠四个类:

  1. Dep 发布订阅模式
  2. Watcher 通知更新,收到通知之后调用update进行更新
  3. Observe 添加响应式属性,遍历 data 转为响应式
  4. Vue.js 将数据转化为响应式的

本节我们主要说明依赖收集的执行过程 根据上一篇文章的描述我们知道每一个对象需要被侦测的时候都都需要通过一个Observer类对其进行响应式侦测(侦测他是否为对象)。而通过浏览器控制台我们打印获取的数据(obj)可以看出我们需要侦测的对象obj身上,每一层对象上都有一个__ob__属性,而这个属性身上都含有一个dep属性。因此我们自然而然的知道,想要监测这个对象的每一个属性,我们必须给他身上添加这个属性,因此我们可以将收集依赖抽象为Dep类。

Dep

在说Dep类之前,我们不得不提到发布订阅模式。简单来说就是一个程序分为发布者和订阅者以及信号中心三种,而我们可以将订阅者收集起来,一起发布。这里Dep类就是使用了这样的一个逻辑。

Dep作为发布者,将订阅者信息收集起来放进subs数组里(subs为订阅者的英文缩写),在发布时发布通知notify提醒watcher类对这个数据进行update更新。

那我们是如何收集依赖呢? 我们可以定义一个全局的Dep.watcher来监控他的内部有哪些属性是正在读取数据的,我们通过收集依赖把它放到我们的添加订阅数组(subs:存放Watcher的实例)中

简单来说就是:

在 get 中添加 Dep.target (观察者)

在 set 中 触发 notify (通知)

Dep代码如下

class Dep {
  constructor() {
    this.subs = []//subs为订阅者,用于存放Watcher的实例化对象
  }

  depend() {//添加依赖
      if(Dep.target){//getter会从全局读取正在读取依赖的Watcher,并把它收集到依赖中,
      //因此我们可以由此改写getter(此处可跳到getter函数)
          this.addSub(Dep.target)//Dep.target实际上是我们自己定义的全局的位置
      }
    
  }

  notify() {//通知更新
    const subs = [...this.subs]
    subs.forEach((s) => s.update())
  }

  addSub(sub) {//添加订阅
    this.subs.push(sub)
  }
}

而我们在Observe类和Vue传数据的时候两次提到了响应式,但其实他们是有一定区别的,比如Observe类,它的主要功能为:遍历data并为他添加响应式的属性;而Vue.js中提到的响应式只是将data拥有的属性都添加到Vue本身,以此方便他使用。

watcher

主要用于数据重渲染,观察者中的必备方法用来更新视图。这里不再赘述。

总结

以上为本人在响应式依赖以及依赖收集的见解。