vue3中computed实现原理(附带源码)面试重点

132 阅读4分钟

vue3中computed实现原理(附带源码)面试重点

主要原理介绍:

computed 实现原理描述:

Vue 3 中的 computed 是基于响应式系统的核心类 ReactiveEffectProxy 代理机制协同实现的。

computed 的核心实现由 ComputedRefImpl 类构成。该类包含一个关键状态标志 _dirty,用于标识计算属性的值是否已过期(即依赖项发生变化),初始值为 true,表示需要首次求值。

ComputedRefImpl 实例化时,会创建一个 ReactiveEffect 实例,并将其赋值给 effect 属性。该 effect 接收用户传入的 getter 函数作为执行逻辑,并传入一个自定义调度函数(scheduler)。这个调度函数的作用是:

  • _dirty 标志重置为 true,标记计算属性为“脏”;
  • 触发相关依赖的更新通知,使视图或其他依赖者重新收集依赖或更新。

🔍 依赖收集阶段(首次读取):

当首次访问 computed 的值(即调用其 get 方法)时,会执行 effect.run() —— 也就是执行用户的 getter 函数。

getter 执行过程中,若访问了其他响应式数据(如 refreactive 对象),就会触发这些响应式对象的 Proxyget 拦截器。

此时,Vue 的依赖收集核心函数 track(target, key) 被调用,其作用是:

  • 判断当前是否有正在执行的 activeEffect(即当前运行的 ReactiveEffect 实例);
  • 若有,则建立依赖关系:以 WeakMap 结构存储为 { target -> Map{ key -> Set(effect) } }
  • 同时将当前 effect 添加到其自身的 deps 数组中,用于后续清理和依赖管理。

这样,computedeffect 就成功收集了它所依赖的响应式数据。


🔄 触发更新阶段(依赖变化):

computed 所依赖的响应式数据发生变化时,会触发其 Proxyset 拦截器,进而调用 trigger(target, key) 函数。

trigger 会根据 targetkey 查找所有依赖该数据的 effect 集合,并依次执行它们的调度逻辑。

对于 computedeffect,其调度函数会被调用,执行以下操作:

  • 设置 _dirty = true,表示计算属性需要重新求值;
  • 触发通知,告知所有依赖该 computed 的副作用(如组件渲染)需要重新执行。

当下一次访问 computed 值时,发现 _dirty === true,便会重新执行 getter 求值,并再次进行依赖收集,完成闭环。


🧠 总结关键点:

模块作用
ComputedRefImpl封装计算属性,管理 _dirty 状态与 effect
ReactiveEffect副作用容器,执行 getter,支持调度函数
track依赖收集:建立 target -> key -> effect 的映射
trigger派发更新:通知依赖的 effect 重新执行或调度
Proxy拦截 get/set,驱动 tracktrigger

✅ 一句话概括:

computed 通过 ReactiveEffect 执行 getter 触发依赖收集(track),并在依赖变更时通过 trigger 调用调度函数标记 _dirtytrue,实现惰性求值与响应式更新。


时序图

截屏2025-08-17 19.14.22.png

核心源码

内置方法effect

function effect(fn, options = {}){
  const _effect = new ReactiveEffect(fn);
  _effect.run();// 默认让响应式的方法执行一次
  const runner = _effect.run.bind(_effect)
  runner.effect = _effect;
  return runner;
}

核心类 ReactiveEffect

let activeEffect;// 激活状态的effect
class ReactiveEffect {
  active = true; // 是否属于激活状态
  deps = []; // 在重新收集依赖之前清楚之前的
  parent = undefined; // 父节点  主要为了解决effect嵌套问题
  constructor(publice fn, private scheduler){}
  run(){
    if(!this.active){
      // 未被激活时,直接运行fn  在get方法里进行依赖收集
      return this.fn()
    }
    try {
      this.parent = activeEffect;
      actiEffect = this;
      cleanupEffect(this);
      return this.fn();
    } finally {
      // 释放
      activeEffect = this.parent;
      this.parent = undefined;
    }
  }
  stop() {
    if(this.active){
      cleanupEffect(this)
      this.active = false;
    }
  }
}

function cleanupEffect(effect) {
  let {deps} = effect;
  for(let i = 0; i< deps.length; i++){
    deps[i].delete(effect);
  }
  effect.deps.length = 0;
}

核心方法依赖收集和更新

const targetMap = new WeakMap()
// 该方法会在取值的get方法里调用
function track(target, key){
  if(!activeEffect){
    // 取值操作没有发生在effect中
    return;
  }
  let depsMap = targetMap.get(target)
  if(!depsMap){
    targetMap.set(target, (deps = new Map()))
  }
  let dep = depsMap.get(key);
  if(!dep){
    depsMap.set(key,(dep = new Set()))
  }
  trackEffects(dep);
}
function trackEffects(dep){
  let shouldTrack = !dep.has(activeEffect)
  if(shouldTrack){
    dep.add(activeEffect);
    activeEffect.deps.push(dep);
  }
}
// 该方法会在set方法里调用
function trigger(target, key, newValue, oldValue){
  // weakMap {obj: map{key: set(effect)}}
  const depsMap = targetMap.get(target);
  if(!depsMap){
    return;
  }
  const dep = depsMap.get(key);
   triggerEffects(dep);
}
function triggerEffects(dep) {
  if(dep){
   const effects = [...dep];
    effects.forEach((effect) => {
      if(activeEffect != effect){
        if(!effect.scheduler){
          effect.run();
        }else{
          effect.scheduler()
        }
      }
    })
  }

}

核心类 ComputedRefImpl

clsaa ComputedRefImpl {
  dep = undefined;
  effect = undefined;
  __v_isRef = true;
  _dirty = true;
  constructor(getter, public setter){
    this.effect = new ReactiveEffect(getter, () => {
      this._dirty = true;
      triggerEffects(this.dep);
    })
  }
  get value(){
    if(activeEffect){
      trackEffects(this.dep || this.dep = new Set())
    }
    if(this._dirty){
      this._value = this.effect.run();
      this._dirty = false
    }
    return this._value;
  }
  set value(newValue){
    this.setter(newValue)
  }
}

computed 函数

const noop = () => {};
function isFunction(obj) {
  return typeof obj === 'function';
}
function computed(getterOrOptions){
  let onlyGetter = isFunction(getterOrOptions)

  let getter;
  let setter;

  if(onlyGetter){
    getter = getterOrOptions;
    setter = noop
  }else{
    getter = getterOrOptions.get;
    setter = getterOrOptions.set || noop
  }
  return new ComputedImpl(getter,setter)
}