在 Vue 中,computed 属性通过依赖追踪和缓存机制实现高效的响应式更新。其核心原理可分解为以下步骤:
1. 初始化阶段
当 Vue 实例化时,会遍历 computed 选项,为每个计算属性创建一个内部 watcher(称为 computed watcher)。与普通 watcher 不同:
惰性求值:
computed watcher不会立即执行计算函数。缓存标记:内部维护
dirty标志位,表示缓存是否失效。
// 伪代码示例(Vue 2.x)
function initComputed(vm) {
const computed = vm.$options.computed;
for (const key in computed) {
const getter = typeof computed[key] === 'function' ? computed[key] : computed[key].get;
// 为每个计算属性创建 watcher
vm._computedWatchers[key] = new Watcher(vm, getter, { lazy: true });
// 将计算属性代理到实例上
defineComputed(vm, key, getter);
}
}2. 依赖收集(当访问计算属性时)
当组件渲染或代码中访问计算属性时(如 this.fullName),会触发其 getter:
检查缓存:若
dirty为false,直接返回缓存值。重新计算:若
dirty为true:执行计算函数(如
return this.firstName + this.lastName)。关键步骤:在执行过程中,访问的响应式数据(如
firstName)会触发它们的 getter,从而将当前computed watcher添加到这些数据的依赖列表中(Dep.target机制)。计算完成后,将结果缓存,并将
dirty设为false。
// 计算属性的 getter 伪代码
function computedGetter() {
const watcher = this._computedWatchers[key];
if (watcher.dirty) {
// 执行计算函数,同时收集依赖
watcher.evaluate(); // 内部调用 watcher.get()
}
// 将当前 computed watcher 作为依赖(用于后续触发更新)
if (Dep.target) {
watcher.depend();
}
return watcher.value;
}3. 依赖更新(当响应式数据变化时)
当计算属性依赖的响应式数据(如 firstName)被修改时:
通知依赖:数据的 setter 会通知所有依赖它的 watcher(包括
computed watcher)。标记缓存失效:
computed watcher将dirty设为true,但不立即重新计算。触发组件更新:如果计算属性在模板中被使用,组件的渲染 watcher 会收到通知,触发重新渲染。
重新计算:在重新渲染过程中再次访问计算属性时,由于
dirty为true,会重新执行计算函数并更新缓存。
// 响应式数据 setter 伪代码
function reactiveSetter(newValue) {
const value = this.value;
if (newValue !== value) {
this.value = newValue;
// 通知所有依赖该数据的 watcher
dep.notify(); // 触发 watcher.update()
}
}
// computed watcher 的 update 方法
update() {
// 仅标记 dirty,不立即计算
this.dirty = true;
// 通知依赖此计算属性的组件重新渲染
if (this.lazy) {
// 如果是 computed watcher,触发依赖它的渲染 watcher
this.dep.notify();
}
}4. 缓存机制的优势
避免重复计算:只要依赖数据未变化,多次访问计算属性直接返回缓存值。
按需更新:依赖数据变化时,仅标记缓存失效,延迟到实际访问时才重新计算。
性能优化:相比
methods,computed在依赖不变时不会重复执行函数。
Vue 3 的改进
在 Vue 3 中,computed 基于 Proxy 和 effect 实现,原理类似但更高效:
使用
ReactiveEffect替代Watcher,通过track(收集依赖)和trigger(触发更新)管理依赖。利用
ref或reactive包装计算结果,通过get value()触发依赖收集。通过
scheduler优化更新流程,避免不必要的计算。
// Vue 3 computed 简化原理
function computed(getter) {
let dirty = true;
let value;
const effect = new ReactiveEffect(getter, () => {
dirty = true; // 依赖变化时标记缓存失效
});
return {
get value() {
if (dirty) {
value = effect.run(); // 重新计算
dirty = false;
}
track(effect); // 收集依赖
return value;
},
};
}总结
这种机制确保了 computed 的高效响应式更新,同时通过缓存避免了不必要的计算开销。