vue计算属性的实现
Vue 计算属性的实现原理
Vue 的计算属性(Computed Properties)是基于响应式依赖进行缓存的派生值。其核心实现依赖于 Vue 的响应式系统和 Watcher 机制。
初始化阶段
在 Vue 组件初始化时,计算属性会通过 initComputed 函数进行初始化。每个计算属性会创建一个对应的 Watcher 实例,并设置 lazy: true 标志,表示这是一个惰性求值的 Watcher(不会立即执行求值)。
function initComputed(vm, computed) {
const watchers = vm._computedWatchers = Object.create(null);
for (const key in computed) {
const getter = computed[key];
watchers[key] = new Watcher(vm, getter, noop, { lazy: true });
defineComputed(vm, key, getter);
}
}
依赖收集
当首次访问计算属性时,会触发 Watcher 的求值(通过 evaluate 方法)。在求值过程中,计算属性的 getter 函数被执行,访问到的响应式数据会将该 Watcher 作为依赖收集起来。
Watcher.prototype.evaluate = function() {
this.value = this.get();
this.dirty = false;
};
缓存机制
计算属性具有缓存特性,只有当其依赖的响应式数据发生变化时才会重新计算。这是通过 dirty 标志实现的:
- 初始时
dirty = true,表示需要重新计算 - 计算完成后
dirty = false,后续访问直接返回缓存值 - 当依赖变化时,会通过
update方法将dirty重置为true
Watcher.prototype.update = function() {
if (this.lazy) {
this.dirty = true;
} else {
queueWatcher(this);
}
};
属性代理
通过 defineComputed 将计算属性代理到组件实例上,使用 createComputedGetter 创建自定义 getter:
function createComputedGetter(key) {
return function computedGetter() {
const watcher = this._computedWatchers[key];
if (watcher.dirty) {
watcher.evaluate();
}
if (Dep.target) {
watcher.depend();
}
return watcher.value;
};
}
触发更新流程
当计算属性的依赖发生变化时:
- 依赖的 setter 触发,通知所有订阅的 Watcher
- 计算属性的 Watcher 收到通知,将
dirty设为 true - 下次访问计算属性时检测到
dirty为 true,触发重新计算 - 计算结果缓存后返回,并通知依赖该计算属性的其他 Watcher
这种设计确保了计算属性只在必要时重新计算,提高了性能,同时保持了响应式特性。







