Vue3源码解读-响应式(1)

266 阅读2分钟

前文

在之前其实我是很少写文章的, 都是读完就完了, 不过最近面试多了发现, 就像我的签名说的, 有些事情你写不出来就说明你没有想清楚. 而只是读完的时候, 我确实是还没有想清楚..

进入正题

首先解释一下我解读源码的方式, 首先当前的大部分代码都是互相嵌套调用的, 所以很多时候我们并没有办法找出线头最开始的地方, 所以我的方式就是从引用最少的地方开始, 一步步抽丝剥茧地找到它的下一步, 再下一步, 直到将所有的内容组合起来, 形成整体. 最后得到我们的理解.

结合官网上面的api用法, 进行对应的解读.

1. 核心

reactivity/src/dep.ts, 依赖收集的实例辅助字段, 主要用于处理依赖收集后的清除旧依赖.
export type Dep = Set<ReactiveEffect> & TrackedMarkers
//  w和n用于重新依赖收集后清除不再依赖的effect
type TrackedMarkers = {
  w: number // 是否track, 通过& trackOpBit 判断

  n: number // 是否新track, 通过& trackOpBit 判断
}

// 设置dep在上一次已经track, 在触发新一轮依赖收集前使用
export const initDepMarkers = ({ deps }: ReactiveEffect) => {
  if (deps.length) {
    for (let i = 0; i < deps.length; i++) {
      deps[i].w |= trackOpBit // set was tracked
    }
  }
}

// 重新进行依赖收集后, 根据w和n值, 清除已经不再依赖的effect, 然后清除标记
export const finalizeDepMarkers = (effect: ReactiveEffect) => {
  const { deps } = effect
  if (deps.length) {
    let ptr = 0
    for (let i = 0; i < deps.length; i++) {
      const dep = deps[i]
      if (wasTracked(dep) && !newTracked(dep)) {
        dep.delete(effect)
      } else {
        deps[ptr++] = dep
      }
      // clear bits
      dep.w &= ~trackOpBit
      dep.n &= ~trackOpBit
    }
    deps.length = ptr
  }
}
reactivity/src/effect.ts 依赖收集的的实例.

vue的依赖收集想必都知道, 首先就是依赖收集的依赖实现函数, 在依赖函数中主要做了两件事情.

  1. 在执行时, 缓存上一个activeEffect并指定当前为active, 然后执行回调函数并且进行依赖收集. 依赖收集完成后 清除旧的依赖内容并且恢复activeEffect
  2. 停止并且清除依赖.

ReactiveEffec是被收集的依赖. 被收集时将其添加到对应的deps列表中.

// 依赖收集中的依赖实现函数.
export class ReactiveEffect<T = any> {
  active = true
  deps: Dep[] = []
  parent: ReactiveEffect | undefined = undefined

  // 如果是一个computed函数, 则会有此属性
  computed?: ComputedRefImpl<T> 

  allowRecurse?: boolean

  private deferStop?: boolean

  onStop?: () => void
  // dev only
  onTrack?: (event: DebuggerEvent) => void
  // dev only
  onTrigger?: (event: DebuggerEvent) => void

  constructor(
    public fn: () => T,
    public scheduler: EffectScheduler | null = null,
    scope?: EffectScope
  ) {
    recordEffectScope(this, scope)
  }

  // 执行effect
  run() {
    if (!this.active) {
      // 如果已经触发了stop, 则立刻执行, 不触发依赖收集, 用于在先触发了stop后异步触发run的情况
      return this.fn()
    }
    let parent: ReactiveEffect | undefined = activeEffect
    let lastShouldTrack = shouldTrack
    // 获取当前
    while (parent) {
      if (parent === this) {
        return
      }
      parent = parent.parent
    }
    try {
      // 临时记录activeEffect
      this.parent = activeEffect
      activeEffect = this
      shouldTrack = true

      // 递归track的层级
      trackOpBit = 1 << ++effectTrackDepth

      if (effectTrackDepth <= maxMarkerBits) {
        initDepMarkers(this)
      } else {
        // 层级过多时先清除再收集依赖的方式进行处理.
        cleanupEffect(this)
      }
      return this.fn()
    } finally {
      if (effectTrackDepth <= maxMarkerBits) {
        // 如果使用init的方式进行收集, 此时需要去掉旧的收集内容.
        finalizeDepMarkers(this)
      }

      trackOpBit = 1 << --effectTrackDepth

      // 恢复activeEffect
      activeEffect = this.parent
      shouldTrack = lastShouldTrack
      this.parent = undefined

      // 如果设置了延后清除, 则再执行一次清除.
      if (this.deferStop) {
        this.stop()
      }
    }
  }

  // 停止时如果正在运行, 则延后清除.
  stop() {
    // stopped while running itself - defer the cleanup
    if (activeEffect === this) {
      this.deferStop = true
    } else if (this.active) {
      cleanupEffect(this)
      if (this.onStop) {
        this.onStop()
      }
      this.active = false
    }
  }
}

依赖收集函数, 在执行watcher的条件或者computed时, 设置activeEffect, 然后在对应的变量触发get时, 添加dep到activeEffect的deps中, 同时添加activeEffect到对应的变量上.

/** 收集依赖, 先预处理依赖map后, 添加到依赖队列 */
export function track(target: object, type: TrackOpTypes, key: unknown) {
  if (shouldTrack && activeEffect) {
    let depsMap = targetMap.get(target)
    if (!depsMap) {
      targetMap.set(target, (depsMap = new Map()))
    }
    let dep = depsMap.get(key)
    if (!dep) {
      depsMap.set(key, (dep = createDep()))
    }

    const eventInfo = __DEV__
      ? { effect: activeEffect, target, type, key }
      : undefined

    trackEffects(dep, eventInfo)
  }
}

/** 收集依赖, 添加到依赖队列中, activeEffect表示正在执行的副作用函数 */
export function trackEffects(
  dep: Dep,
  debuggerEventExtraInfo?: DebuggerEventExtraInfo
) {
  let shouldTrack = false
  if (effectTrackDepth <= maxMarkerBits) {
    if (!newTracked(dep)) {
      dep.n |= trackOpBit // set newly tracked
      shouldTrack = !wasTracked(dep)
    }
  } else {
    // Full cleanup mode.
    shouldTrack = !dep.has(activeEffect!)
  }

  if (shouldTrack) {
    dep.add(activeEffect!)
    activeEffect!.deps.push(dep)
    if (__DEV__ && activeEffect!.onTrack) {
      activeEffect!.onTrack({
        effect: activeEffect!,
        ...debuggerEventExtraInfo!
      })
    }
  }
}

触发回调函数与依赖收集刚好对应

/** 触发回调 */
export function trigger(
  target: object,
  type: TriggerOpTypes,
  key?: unknown,
  newValue?: unknown,
  oldValue?: unknown,
  oldTarget?: Map<unknown, unknown> | Set<unknown>
) {
  const depsMap = targetMap.get(target)
  if (!depsMap) {
    // never been tracked
    return
  }

  let deps: (Dep | undefined)[] = []
  if (type === TriggerOpTypes.CLEAR) {
    // collection being cleared
    // trigger all effects for target
    deps = [...depsMap.values()]
  } else if (key === 'length' && isArray(target)) {
    const newLength = toNumber(newValue)
    depsMap.forEach((dep, key) => {
      if (key === 'length' || key >= newLength) {
        deps.push(dep)
      }
    })
  } else {
    // schedule runs for SET | ADD | DELETE
    if (key !== void 0) {
      deps.push(depsMap.get(key))
    }

    // also run for iteration key on ADD | DELETE | Map.SET
    switch (type) {
      case TriggerOpTypes.ADD:
        if (!isArray(target)) {
          deps.push(depsMap.get(ITERATE_KEY))
          if (isMap(target)) {
            deps.push(depsMap.get(MAP_KEY_ITERATE_KEY))
          }
        } else if (isIntegerKey(key)) {
          // new index added to array -> length changes
          deps.push(depsMap.get('length'))
        }
        break
      case TriggerOpTypes.DELETE:
        if (!isArray(target)) {
          deps.push(depsMap.get(ITERATE_KEY))
          if (isMap(target)) {
            deps.push(depsMap.get(MAP_KEY_ITERATE_KEY))
          }
        }
        break
      case TriggerOpTypes.SET:
        if (isMap(target)) {
          deps.push(depsMap.get(ITERATE_KEY))
        }
        break
    }
  }

  const eventInfo = __DEV__
    ? { target, type, key, newValue, oldValue, oldTarget }
    : undefined

  if (deps.length === 1) {
    if (deps[0]) {
      if (__DEV__) {
        triggerEffects(deps[0], eventInfo)
      } else {
        triggerEffects(deps[0])
      }
    }
  } else {
    const effects: ReactiveEffect[] = []
    for (const dep of deps) {
      if (dep) {
        effects.push(...dep)
      }
    }
    if (__DEV__) {
      triggerEffects(createDep(effects), eventInfo)
    } else {
      triggerEffects(createDep(effects))
    }
  }
}

/** 触发回调计算, 先计算computed属性, 再计算别的属性 */
export function triggerEffects(
  dep: Dep | ReactiveEffect[],
  debuggerEventExtraInfo?: DebuggerEventExtraInfo
) {
  const effects = isArray(dep) ? dep : [...dep]
  for (const effect of effects) {
    if (effect.computed) {
      triggerEffect(effect, debuggerEventExtraInfo)
    }
  }
  for (const effect of effects) {
    if (!effect.computed) {
      triggerEffect(effect, debuggerEventExtraInfo)
    }
  }
}

未完待续...