[Vue3 源码阅读] effect.ts

517 阅读2分钟

一、介绍

之前看ref源码,发现其是通过track进行依赖收集,通过trigger进行更新通知的。这两个函数都定义在effect.ts文件中。

二、源码分析

2.1: track

// packages\reactivity\src\effect.ts

// 当前活跃的副作用,类似于vue2中的watcher
let activeEffect: ReactiveEffect | undefined

// 用于储存 原始对象和activeEffect的依赖关系
// 是一个2维的map
// 第一层的map: key是原始对象,value是一个map
// 第二层的map: key是原始对象的属性名称,value是该属性的依赖集合
const targetMap = new WeakMap<any, KeyToDepMap>()

// 第一个参数:原始对象
// 第二个参数:类型
// 第三个参数:原始对象的属性名称,即key
export function track(target: object, type: TrackOpTypes, key: unknown) {
  // 判断是否应该进行track
  if (!shouldTrack || activeEffect === undefined) {
    return
  }
  
  // 当前targetMap中有没有存储该原始对象的依赖关系
  let depsMap = targetMap.get(target)
  if (!depsMap) {
    // 如果没有存储,就把该原始对象作为key,值是一个新建的空map
    targetMap.set(target, (depsMap = new Map()))
  }
  // 有没有key对应的依赖集合
  let dep = depsMap.get(key)
  if (!dep) {
    // 如果没有依赖集合,就新建一个空的set存储依赖
    depsMap.set(key, (dep = new Set()))
  }
  // 判断依赖集合中有没有当前应该收集的activeEffect
  if (!dep.has(activeEffect)) {
    // 如果当前activeEffect没有被收集,则应该收集
    dep.add(activeEffect)
    // activeEffect的依赖中也进行同步收集,相当于两边相互收集了对方
    activeEffect.deps.push(dep)
  }
}

总结:track方法主要是维护targetMap,targetMap中维护了原始对象各属性应该收集的activeEffect。

2.2 trigger

export function trigger(
  target: object,
  type: TriggerOpTypes,
  key?: unknown,
  newValue?: unknown,
  oldValue?: unknown,
  oldTarget?: Map<unknown, unknown> | Set<unknown>
) {
  const depsMap = targetMap.get(target)
  // 该属性从未被track,不需要触发更新通知,直接返回
  if (!depsMap) {
    // never been tracked
    return
  }
  
  // 定义一个set,存储需要通知的activeEffect
  const effects = new Set<ReactiveEffect>()
  // 定义add方法
  const add = (effectsToAdd: Set<ReactiveEffect> | undefined) => {
    if (effectsToAdd) {
      effectsToAdd.forEach(effect => {
        if (effect !== activeEffect || effect.allowRecurse) {
          effects.add(effect)
        }
      })
    }
  }
  
  // clear类型的变更,通知所有的依赖项
  if (type === TriggerOpTypes.CLEAR) {
    // collection being cleared
    // trigger all effects for target
    depsMap.forEach(add)
  } else if (key === 'length' && isArray(target)) {
    // 数组长度类型的变更,通知超出新长度的key对应的依赖项
    depsMap.forEach((dep, key) => {
      if (key === 'length' || key >= (newValue as number)) {
        add(dep)
      }
    })
  } else {
    // schedule runs for SET | ADD | DELETE
    if (key !== void 0) {
      add(depsMap.get(key))
    }
  }
  
  // 定义run方法,执行effect更新
  const run = (effect: ReactiveEffect) => {
    if (effect.options.scheduler执行) {
      // 通过scheduler执行
      effect.options.scheduler(effect)
    } else {
      // 直接执行
      effect()
    }
  }
  
  // 对所有应该通知的依赖项执行run方法
  effects.forEach(run)
}

总结:trigger方法根据type确定哪些key对应的依赖项应该被更新,并执行依赖项的更新。