vue3 依赖收集和派发更新

846 阅读1分钟

前言

最近在看vue3 的源码时,遇到了两个概念    依赖收集和派发更新,首先顶级两个全局变量,用来存放和定位追踪的依赖,及track和trigger使用的仓库

let targetMap = new WeakMap();
let activeEffect;

依赖收集

作用

track(obj, 'get', 'x');

track 会去找 obj.x 是否被追踪,如果没找到就将obj.x放入targetMap(完成追踪任务),将 obj.x 作为 map 的 key 将 activeEffect 作为 map 的 value。

抛开取值异常处理之类的,track 只做了一件事,将activeEffect塞入targetMap;

从这,我们可了解targetMap的格式如下

targetMap -> WeakMap()

名称keyvalue
targetMap属性名称Map()
Map属性名称Set()

track源码

function track(target, key) {
  // 首先找 obj 是否有被追踪
  let depsMap = targetMap.get(target);
  if (!depsMap) {
    // 如果没有被追踪,那么添加一个
        targetMap.set(target, (depsMap = new Map()));
  }
  // 然后寻找 obj.x 是否被追踪
  let dep = depsMap.get(key);
    if (!dep) {
    // 如果没有被追踪,那么添加一个
    depsMap.set(key, (dep = new Set()));
  }
  // 如果没有添加 activeEffect 那么添加一个
  if (!dep.has(activeEffect)) {
        dep.add(activeEffect);
    activeEffect.deps.push(dep);
        if (activeEffect.options.onTrack) {
            activeEffect.options.onTrack({
                effect: activeEffect,
                target,
                type,
                key
            });
        }
    }
}

派发更新

作用

然后就是写一个 trigger,还记得trigger在vue是如何调用的吗?

trigger(obj, 'set', 'x')

trigger 只会去 targetMap 中寻找obj.x的追踪任务,如果找到了就去重,然后执行任务。

也就是说:抛开取值异常相关,trigger 也只做了一件事:从 targetMap 取值然后调用该函数值。

源码中此时会给该响应式数据根据类型遍历添加相应的方法

源码

function trigger(target, key) {
  // 寻找追踪项
  const depsMap = targetMap.get(target);
  // 没找到就什么都不干
  if (!depsMap) return;
  // 去重
  const effects = new Set()
  depsMap.get(key).forEach(e => effects.add(e))
  // 执行
  effects.forEach(e => e())
}