Vue响应式原理中主要类的理解

83 阅读2分钟

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

Dep类,依赖管理

作用概述

Dep类主要有两个作用,即:

  1. 依赖收集depend()
  2. 派发更新依赖通知Watcher,notify()

以上可以统称为依赖的收集通知更新Dep 可以理解为对依赖的一种管理

Dep与Watcher的关联性: Dep收集Watcher需要的依赖在哪使用、对应的使用场景等。

源码分析

现在我们可以看一下Dep的源码

let uid = 0; //作为一个唯一的索引
class Dep {
  constructor() {
    this.id = uid++;
    this.subs = []
  }
  // 依赖收集
  depend() {//向订阅者数组里添加订阅者
    if(Dep.target) {
      // Dep.target是当前的watcher,将当前的依赖推到subs中
      this.subs.push(Dep.target)
    }
  }
  // 派发更新
  notify() {//通知更新
    const subs = this.subs.slice();
    for (var i = 0, l = subs.length; i < l; i++) { 
      // 遍历dep中的依赖,对每个依赖执行更新操作
      subs[i].update();
    }
  }
}

Dep.target = null;

收集依赖

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

还记得我们的数据拦截吗defineReactive函数,每一个我们实例化为Observe的数据都拥有一个getter和setter,而一开始即实例化一个Dep实例,这里的getter是一个重写的getter(setter也一样),在重写的getter方法中会进行依赖的收集,也就是调用dep.depend的方法。在setter阶段,比较两个数不同后,会调用依赖的派发更新。即dep.notify

getter --> dep.depend依赖管理,通过依赖收集拿到我需要的watcher
setter --> dep.notify提醒更新

派发更新

那么Watcher是如何实现派发更新的呢? 派发更新分为两部分,更新依赖创建依赖

更新依赖:可以理解为原来的数据被重写

创建依赖:new一个新数据(一个新的watcher)

派发更新update函数会先查看该传入数据的类型是不是对象(即需不需要重新收集依赖)

Watcher类,依赖收集

作用概述

watcher有一个主要的功能,即创建依赖。那么你可能会问——创建依赖和收集依赖我区别不开啊?没关系,这里我将针对依赖收集和创建依赖进行解析

首先,我必须说明什么是依赖,本人对依赖的理解为:每一个Watcher实例就是一个依赖。

创建依赖是什么:指数据在哪需要被使用就需要一个依赖。依赖收集发生在Object.defineProperty()getter中。

依赖收集是什么:订阅数据变化的 Watcher 收集的过程(具体收集过程为,将dep.target推入栈,变成一个又一个watcher实例),这里需要和之前说过的依赖收集联系起来。

Observe类

以上两个类其实都包含在Observe中,observe作为观察者类,对象只要是设有__ob__属性,即可在她身上找到getter、setter并可以成功调用,换句话说也就是拥有这样的属性的数据即可实现依赖收集与派发。

graph TD
Observe类 --> getter
Observe类 --> setter
getter --> 调用dep.depend
setter --> 调用dep.notify

总结

本节主要是针对之前对于派发依赖、依赖收集以及创建依赖三者关系的不清晰以及Watcher、Dep、Observe三者之间的关系进行了。