📚前端面试题速记
Q13

Vue 3 的 computed 是如何实现的?

速记答案(30 秒)

本质是一个带缓存(lazy)的 effect:首次访问或依赖变化时才执行 getter。内部用 dirty 标记 + 缓存值:依赖触发时只把 dirty 设为 true;下次访问时才重新计算。

📖 详细讲解

标准面试回答(推荐记住)

computed 本质是一个惰性的 effect。内部维护 dirty 标记和缓存值,首次访问或依赖变化时才执行 getter。当依赖变化时,只是把 dirty 设为 true,并不立即计算;下次读取 computed 值时才真正重新计算,这样实现了缓存和按需更新。

实现细节

ComputedRefImpl
内部包含一个 ReactiveEffect,但配置了 lazy: true 和自定义的 scheduler

执行流程:

  1. 初始化:创建 Effect,但因为 lazy 不会立即执行。dirty 设为 true。
  2. 首次读取:触发 Getter -> 检测到 dirty 为 true -> 执行 Effect.run() -> 计算新值并缓存 -> dirty 设为 false -> 收集依赖(track)。
  3. 依赖更新:触发 Trigger -> 执行 Scheduler -> 将 dirty 设为 true -> 触发 computed 自身的 Trigger(通知依赖这一计算属性的效果)。
  4. 再次读取:检测到 dirty 为 false -> 直接返回缓存值。

这种机制保证了只有在依赖变化且被读取时才会重新计算,避免无效计算。

面试要点

  • 理解惰性计算和缓存机制
  • 知道 dirty 标记的作用
  • 能解释 computed 的性能优势

💻 代码示例

computed 原理简化
// computed 简化实现
function computed<T>(getter: () => T) {
  let value: T
  let dirty = true  // 脏标记
  
  // 创建 effect,但不立即执行
  const effect = new ReactiveEffect(getter, () => {
    // 依赖变化时的调度器
    if (!dirty) {
      dirty = true
      // 触发依赖此 computed 的更新
      trigger(computedRef, 'value')
    }
  })
  
  const computedRef = {
    get value() {
      if (dirty) {
        value = effect.run()
        dirty = false
      }
      return value
    }
  }
  
  return computedRef
}