◆

Vue3 reactive 代理与依赖收集完整流程

2023-02-02 01:370

前置流程

  • 首先判断是否为对象
  • 会有一个 map 变量去存储被 reactive 代理过的对象,已经被 reactive 代理过的,直接 return
  • 如果上述没有,进入到 proxy 进行代理
    • 进行代理时,需要传入一个 handler,handler 主要处理 set 与 get
    • 在 get 时,通过 Reflect.get 拿到 target 对应的 key 值
    • 由于 readonly 的实现也基本上是通过这个 handler,但是由于是只读,所以不需要进行依赖收集
    • 接下来如果不是 readonly 那么进行依赖收集

依赖收集

  • 依赖收集需要一个数据结构去存储当前的依赖,如 targetMap,结构如下
  • 它是一个 weakMap 对象,它的 key 就是被代理的对象,value 又是一个 map 对象
  • value 对象的 map 对象,key 是,被代理对象的属性值,value 则是一个 Set 对象
  • value 中的 set 对象,主要存储的是什么呢?就是当前这个响应式对象的属性值相对应要执行的方法,也就是用户外部定义的方法
WeakMap = {
  源对象
  Map:[
      {
          "key": "name",
          "value": Set[ReactiveEffect]
      },
      {
          "key": "age",
          "value" Set[ReactiveEffect]
      }
  ]
}
  • 接着进入到依赖收集函数,首先会有一个判断当前是否需要进行依赖收集
    • 这里就需要注意了,依赖收集是什么时候进行的呢,只是定义 reactive 需要进行依赖收集吗?
    • 当然进行依赖收集的时候一定是我们获取 target 对象属性的时候,也就是调用了 get 方法的时候
    • 相当于我们要触发用户使用到 get 的地方,也就是用户使用时的代码
  • 我们会使用一个 effect 函数去触发用户执行的代码。
  • 在这个函数内我们做了什么操作呢?我们去实例化一个 ReactiveEffect 类
    • 这个类又是干什么的呢?我们在这个类中增加了一个 run 方法
    • Todo active
    • 在 run 方法内有一个 shouldTrack 表示,当前需要收集依赖,我们将此变量置位 true
    • 同时在运行 run 方法时,我们将当前的这个 ReactiveEffect 类的值赋给一个变量 activeEffect
    • 当上述两步操作完成后,我们执行用户外部传入的操作 reactive 数据的方法 fn
    • 一般在执行用户传入的 fn 方法时,都会涉及到获取 reactive 的数据,即调用 proxy 的 get,或者设置数据调用了 proxy 的 set
    • 这是我们先分析 get 因为在 get 中我们要开始进行依赖收集了,所以首先我们要判断是否执行了 effect 如果没有执行是不需要进行依赖收集的,我们用 shouldTrack 为 true 以及 activeEffect 有值表示当前应该进行依赖收集
  • 进行依赖收集时,要判断当前的对象是否已经在依赖收集的 targetMap 中
  • 如果当前的对象不在 targetMap 中说明之前没有对此对象进行依赖收集,那么我们将要在 targetMap 添加它,key 为 target,value 是一个空 map 对象(new Map())
  • 然后如果当前的对象在 targetMap 中,则取出它对应的 value 假设为 depsMap
  • 然后我们要从 depsMap 中获取当前指定属性对应的 value 值,假设这个获取到的值为 dep
  • 如果当前对象没有进行过依赖收集或者当前对象进行过依赖收集,但是当前的这个属性没有进行收集过,则我们给 dep 一个默认值 new Set()
  • 如果当前的对象对应的属性没有进行过依赖收集,那么当前的 dep 是一个空 Set 集合,这时候我们吧当前的 activeEffect 也就是 ReactiveEffect 添加到这个 set 中
  • 同时我们给 activeEffect 中的一个 deps 属性添加这个 dep
  • 如果当前的对象对应的属性进行过依赖收集,我们去判断这个 dep 是否已经添加过 activeEffect 如果已经添加过,则不需要继续添加了
  • 至此依赖收集完毕

触发收集的依赖

  • 收集依赖最终是为了进行触发,什么情况下触发呢,肯定是在 set 操作中进行触发
  • 首先我们在触发依赖时,需要从 targetMap 中获取当前的代理对象
  • 当我们获取到当前的代理对象时,我们需要获取当前代理对象的对应属性对应的 value 也就是 dep 值
  • dep 值是一个 set 集合,我们将其转换为数组后,进行循环执行里面的 run 方法,也就是用户外部的方法

一些注意的点

  • reactive 进行代理的时候如果获取到的值是对象,则再进行一次 reactive 代理,这样就实现了深层次的代理
  • 基于以上的思路有些 api 的设计理解起来就比较简单了

readonly

相当于在 reactive 进行 proxy 代理,触发 get 时,不对其进行依赖收集,因为其不需要触发 trigger

然后我们在 set 时抛出一个警告即可

shallowReactive && shallowReadonly

相当于在 reactive 代理时,如果获取到的是对象,则下面的对象没有进行 proxy 代理,也就是下面的深层次对象不是响应式的

toRaw

isProxy isReactive isReadonly

我们创建一个枚举值用来判断当前的数据类型

export const enum ReactiveFlags {
  IS_REACTIVE = '__v_isReactive',
  IS_READONLY = '__v_isReadonly',
  RAW = '__v_raw',
}
💬留言板0

还没有留言,来发表第一条吧~

PAGE LOAD / LIVEVue3 reactive 代理与依赖收集完整流程加载中