Vue Reactive 使用 Javascript Proxy 代理对象来实现依赖追踪和触发更新,在 ProxyHandler 实现中,get 方法进行依赖追踪,set 方法进行触发更新:

一、mutableHandlers → MutableReactiveHandler extends BaseReactiveHandler implements ProxyHandler

查看 vue 3.4 源码 (packages/reactivity/src/baseHandlers.ts):

1export const mutableHandlers: ProxyHandler<object> =
2  /*#__PURE__*/ new MutableReactiveHandler()
3
4class MutableReactiveHandler extends BaseReactiveHandler {
5  /// ...
6  set(
7    target: object,
8    key: string | symbol,
9    value: unknown,
10    receiver: object,
11  ): boolean {
12    /// ...
13  }
14  /// ...
15}
16
17class BaseReactiveHandler implements ProxyHandler<Target> {
18  /// ...
19  get(target: Target, key: string | symbol, receiver: object) {
20    /// ...
21  }
22  /// ...
23}

 

二、get 方法进行依赖追踪

调用 track 方法:记录哪些 effect 依赖于哪些属性

1class BaseReactiveHandler implements ProxyHandler<Target> {
2  /// ...
3  get(target: Target, key: string | symbol, receiver: object) {
4    /// ...
5    if (!isReadonly) {
6      track(target, TrackOpTypes.GET, key)
7    }
8    /// ...
9  }
10  /// ...
11}

 

通过 trackEffect 方法,记录当前活动的 effect 到依赖集合中

shouldTrack && activeEffect:判断是否应该进行跟踪以及当前是否有活动的 effect

1export function track(target: object, type: TrackOpTypes, key: unknown) {
2  if (shouldTrack && activeEffect) {
3    /// ...
4    trackEffect(
5      activeEffect,
6      dep,
7      __DEV__
8        ? {
9          target,
10          type,
11          key,
12        }
13        : void 0,
14    )
15  }
16}

 

ReactiveEffect (effect 类),用于表示依赖关系

Dep,依赖集合,用于存储依赖该集合的所有 effect

dep.set(effect, effect._trackId):核心,将 effect 的 _trackId 添加到依赖集合中

1export function trackEffect(
2  effect: ReactiveEffect,
3  dep: Dep,
4  debuggerEventExtraInfo?: DebuggerEventExtraInfo,
5) {
6  if (dep.get(effect) !== effect._trackId) {
7    /// ...
8    dep.set(effect, effect._trackId)
9    /// ...
10  }
11}

 

三、set 方法进行触发更新

调用 trigger 方法:找到所有依赖于该属性的 effect,并触发这些 effect

1class MutableReactiveHandler extends BaseReactiveHandler {
2  /// ...
3  set(
4    target: object,
5    key: string | symbol,
6    value: unknown,
7    receiver: object,
8  ): boolean {
9    /// ...
10    if (target === toRaw(receiver)) {
11      if (!hadKey) {
12        trigger(target, TriggerOpTypes.ADD, key, value)
13      } else if (hasChanged(value, oldValue)) {
14        trigger(target, TriggerOpTypes.SET, key, value, oldValue)
15      }
16    }
17    /// ...
18  }
19  /// ...
20}

 

通过 triggerEffects 方法:触发依赖集合中的所有 effect

1export function trigger(
2  target: object,
3  type: TriggerOpTypes,
4  key?: unknown,
5  newValue?: unknown,
6  oldValue?: unknown,
7  oldTarget?: Map<unknown, unknown> | Set<unknown>,
8) {
9  /// ...
10  for (const dep of deps) {
11    if (dep) {
12      triggerEffects(
13        dep,
14        DirtyLevels.Dirty,
15        __DEV__
16          ? {
17            target,
18            type,
19            key,
20            newValue,
21            oldValue,
22            oldTarget,
23          }
24          : void 0,
25      )
26    }
27  }
28  /// ...
29}

 

Dep: 依赖集合,用于存储依赖该集合的所有 effect

DirtyLevels: 脏度级别的枚举,表示 effect 的脏度状态

queueEffectSchedulers: 一个数组,用于存储需要稍后运行的调度器(schedulers)

effect.scheduler :调度器,一个函数,用于决定何时以及如何运行某个 effect

1export type EffectScheduler = (...args: any[]) => any
2
3const queueEffectSchedulers: EffectScheduler[] = []
4
5export function triggerEffects(
6  dep: Dep,
7  dirtyLevel: DirtyLevels,
8  debuggerEventExtraInfo?: DebuggerEventExtraInfo,
9) {
10  pauseScheduling()
11  for (const effect of dep.keys()) {
12    /// ...
13    if (
14      effect._shouldSchedule &&
15      (tracking ??= dep.get(effect) === effect._trackId)
16    ) {
17      /// ...
18      if (
19        (!effect._runnings || effect.allowRecurse) &&
20        effect._dirtyLevel !== DirtyLevels.MaybeDirty_ComputedSideEffect
21      ) {
22        effect._shouldSchedule = false
23        if (effect.scheduler) {
24          queueEffectSchedulers.push(effect.scheduler)
25        }
26      }
27    }
28  }
29  resetScheduling()
30}
31
32export function resetScheduling() {
33  pauseScheduleStack--
34  while (!pauseScheduleStack && queueEffectSchedulers.length) {
35    queueEffectSchedulers.shift()!()
36  }
37}

 

四、其他

学习的记录,仅供参考