Vue3 的响应式系统源码级 computed 的实现原理与缓存机制
字数 1251 2025-12-09 23:09:33

Vue3 的响应式系统源码级 computed 的实现原理与缓存机制

描述:
Vue3 的 computed 是计算属性 API,它会根据其依赖的响应式数据自动计算值,并缓存计算结果,只有在依赖变化时才重新计算。理解其实现原理需要深入分析 effect 系统、缓存机制和依赖收集过程。

解题过程:

1. computed 的基本概念

  • 计算属性是基于其他响应式状态计算出的值
  • 具有惰性求值特性:只有实际访问时才计算
  • 具有缓存机制:依赖未变化时直接返回缓存值
  • 返回值是一个响应式的 ref 对象,可以通过 .value 访问

2. computed 函数的基本结构
在 Vue3 源码中,computed 函数接收一个 getter 函数或包含 get/set 的对象:

// reactivity/computed.ts
export function computed<T>(
  getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
) {
  let getter: ComputedGetter<T>
  let setter: ComputedSetter<T>
  
  // 判断参数类型
  if (isFunction(getterOrOptions)) {
    getter = getterOrOptions
    setter = NOOP // 只读计算属性
  } else {
    getter = getterOrOptions.get
    setter = getterOrOptions.set || NOOP
  }
  
  return new ComputedRefImpl(
    getter,
    setter,
    isFunction(getterOrOptions) || !getterOrOptions.set
  ) as any
}

3. ComputedRefImpl 核心类实现
这是 computed 的核心类,实现了计算逻辑和缓存机制:

class ComputedRefImpl<T> {
  // 响应式相关的属性
  public dep?: Dep
  private _value!: T
  private _dirty = true  // 脏标记,表示是否需要重新计算
  public readonly effect: ReactiveEffect<T>
  public readonly __v_isRef = true
  public readonly [ReactiveFlags.IS_READONLY]: boolean
  
  constructor(
    getter: ComputedGetter<T>,
    private readonly _setter: ComputedSetter<T>,
    isReadonly: boolean
  ) {
    // 创建 effect,但不立即执行
    this.effect = new ReactiveEffect(
      getter,
      () => {
        // 调度器函数,当依赖变化时被调用
        if (!this._dirty) {
          this._dirty = true
          // 触发依赖此 computed 的 effect
          triggerRefValue(this)
        }
      }
    )
    this.effect.computed = this
    this[ReactiveFlags.IS_READONLY] = isReadonly
  }
  
  get value() {
    // 收集依赖
    trackRefValue(this)
    
    // 如果脏标记为 true,需要重新计算
    if (this._dirty) {
      this._dirty = false
      // 执行 effect 获取新值
      this._value = this.effect.run()!
    }
    return this._value
  }
  
  set value(newValue: T) {
    this._setter(newValue)
  }
}

4. 缓存机制的实现原理
计算属性的缓存机制通过以下方式实现:

脏标记 (_dirty) 机制:

  • _dirty 初始为 true,表示需要计算
  • 当访问 value 时,如果 _dirtytrue,则执行计算,然后设为 false
  • 当计算属性的依赖发生变化时,调度器函数会将 _dirty 设为 true

依赖收集与触发流程:

// 访问 computed.value 时的流程:
1. trackRefValue(this)        // 收集当前 activeEffect 对 computed 的依赖
2. 检查 _dirty
   - 如果为 true: 
     a. 执行 this.effect.run()  // 计算新值
     b. 在计算过程中会收集 computed 内部对响应式数据的依赖
     c.  _dirty 设为 false
     d. 缓存计算结果到 _value
   - 如果为 false:
     直接返回缓存的 _value
3. 返回 _value

// 当依赖变化时的流程:
1. 依赖的响应式数据变化触发 computed 的调度器函数
2. 调度器函数将 _dirty 设为 true
3. 触发所有依赖此 computed  effect

5. 惰性计算机制

  • 计算属性不会在创建时立即计算
  • 只有在实际访问 .value 时才进行计算
  • 这是通过不在构造函数中执行 effect,而是在 getter 中按需执行实现的

6. 只读 vs 可写计算属性

// 只读计算属性
const readOnlyComputed = computed(() => count.value * 2)

// 可写计算属性
const writableComputed = computed({
  get: () => count.value * 2,
  set: (val) => { count.value = val / 2 }
})

可写计算属性通过 _setter 函数实现,在设置值时调用用户定义的 setter 函数。

7. 依赖关系图
计算属性建立了两层依赖关系:

外部 effect → computed (通过 trackRefValue)
computed → 内部依赖 (通过 computed 的 effect)

当内部依赖变化时:

  1. 触发 computed 的调度器
  2. 调度器将 _dirty 设为 true
  3. 触发外部 effect
  4. 外部 effect 重新执行时,再次访问 computed.value
  5. 此时 _dirty 为 true,重新计算值

8. 性能优化点

  1. 避免重复计算:只要依赖不变化,多次访问都返回缓存值
  2. 懒计算:不访问就不计算
  3. 惰性更新:依赖变化时不立即计算,等到访问时才计算
  4. 精确更新:只有当依赖变化且计算属性被使用时才重新计算

9. 与普通 effect 的区别

  1. 懒执行:computed 不会自动运行,需要访问才执行
  2. 有缓存:依赖不变时直接返回缓存值
  3. 返回值:computed 返回一个 ref 对象,effect 不返回值
  4. 调度机制:computed 有特殊的调度器处理依赖变化

总结:
Vue3 的 computed 通过 ComputedRefImpl 类实现,核心是脏标记机制和两层依赖收集。它结合了 effect 系统的响应式能力和智能缓存机制,既保证了响应式更新,又避免了不必要的重复计算,是 Vue3 响应式系统中性能优化的重要体现。

Vue3 的响应式系统源码级 computed 的实现原理与缓存机制 描述: Vue3 的 computed 是计算属性 API,它会根据其依赖的响应式数据自动计算值,并缓存计算结果,只有在依赖变化时才重新计算。理解其实现原理需要深入分析 effect 系统、缓存机制和依赖收集过程。 解题过程: 1. computed 的基本概念 计算属性是基于其他响应式状态计算出的值 具有惰性求值特性:只有实际访问时才计算 具有缓存机制:依赖未变化时直接返回缓存值 返回值是一个响应式的 ref 对象,可以通过 .value 访问 2. computed 函数的基本结构 在 Vue3 源码中, computed 函数接收一个 getter 函数或包含 get/set 的对象: 3. ComputedRefImpl 核心类实现 这是 computed 的核心类,实现了计算逻辑和缓存机制: 4. 缓存机制的实现原理 计算属性的缓存机制通过以下方式实现: 脏标记 (_ dirty) 机制: _dirty 初始为 true ,表示需要计算 当访问 value 时,如果 _dirty 为 true ,则执行计算,然后设为 false 当计算属性的依赖发生变化时,调度器函数会将 _dirty 设为 true 依赖收集与触发流程: 5. 惰性计算机制 计算属性不会在创建时立即计算 只有在实际访问 .value 时才进行计算 这是通过不在构造函数中执行 effect,而是在 getter 中按需执行实现的 6. 只读 vs 可写计算属性 可写计算属性通过 _setter 函数实现,在设置值时调用用户定义的 setter 函数。 7. 依赖关系图 计算属性建立了两层依赖关系: 当内部依赖变化时: 触发 computed 的调度器 调度器将 _dirty 设为 true 触发外部 effect 外部 effect 重新执行时,再次访问 computed.value 此时 _dirty 为 true,重新计算值 8. 性能优化点 避免重复计算 :只要依赖不变化,多次访问都返回缓存值 懒计算 :不访问就不计算 惰性更新 :依赖变化时不立即计算,等到访问时才计算 精确更新 :只有当依赖变化且计算属性被使用时才重新计算 9. 与普通 effect 的区别 懒执行 :computed 不会自动运行,需要访问才执行 有缓存 :依赖不变时直接返回缓存值 返回值 :computed 返回一个 ref 对象,effect 不返回值 调度机制 :computed 有特殊的调度器处理依赖变化 总结: Vue3 的 computed 通过 ComputedRefImpl 类实现,核心是脏标记机制和两层依赖收集。它结合了 effect 系统的响应式能力和智能缓存机制,既保证了响应式更新,又避免了不必要的重复计算,是 Vue3 响应式系统中性能优化的重要体现。