Vue中的computed和watch的原理解析
更新时间:2025年12月01日 09:10:13 作者:BUG 饲养员
Vue的computed和watch都是响应式系统的重要组成部分,computed通过依赖缓存提高性能,而watch用于监听数据变化并执行副作用,两者核心机制不同,而watch则是监听变化并触发回调,本文介绍Vue中的computed和watch的原理,感兴趣的朋友跟随小编一起看看吧
一、核心基础:响应式系统支撑
computed与watch的实现均依赖Vue响应式核心——依赖收集与触发更新:
- 响应式数据通过
Object.defineProperty(Vue2)或Proxy(Vue3)劫持 getter/setter; - 读取数据时收集依赖(记录使用该数据的组件/计算属性);
- 修改数据时触发setter,通知所有依赖执行更新逻辑。
二者本质是响应式系统的“上层应用”,差异在于对依赖变化的处理逻辑。
二、computed:带缓存的依赖推导
2.1 核心原理:缓存+懒执行
computed是“派生值”计算器,核心特性是依赖不变则复用缓存,避免重复计算:
- 依赖收集:首次访问computed时,执行其getter函数,收集依赖的响应式数据;
- 缓存结果:将计算结果缓存,标记为“清洁”状态;
- 懒更新:仅当依赖数据变化时,才标记为“脏”状态,下次访问时重新计算并更新缓存。
2.2 简化实现代码
// 模拟computed核心逻辑(Vue3风格)
class ComputedRef {
constructor(getter, ctx) {
this.getter = getter; // 计算函数
this.ctx = ctx; // 上下文
this._value = null; // 缓存值
this._dirty = true; // 脏标记:true需重新计算
}
// 访问computed时触发
get value() {
if (this._dirty) {
this._value = this.getter.call(this.ctx); // 执行计算
this._dirty = false; // 标记为清洁
}
return this._value; // 复用缓存
}
// 依赖变化时调用
markDirty() {
this._dirty = true; // 标记为脏
}
}
// 实际使用
const count = ref(1);
// 计算属性:依赖count
const doubleCount = new ComputedRef(() => count.value * 2, this);
console.log(doubleCount.value); // 2(计算并缓存)
console.log(doubleCount.value); // 2(直接复用缓存)
count.value = 2; // 触发count的setter,调用doubleCount.markDirty()
console.log(doubleCount.value); // 4(重新计算)三、watch:数据变化的副作用触发器
3.1 核心原理:监听变化+执行回调
watch是“数据监听器”,核心是监听指定数据源变化,触发自定义副作用(如异步请求、DOM操作),支持深度监听和立即执行。
3.2 关键特性与实现
// 模拟watch核心逻辑(Vue3风格)
function watch(source, callback, options = {}) {
const { immediate = false, deep = false } = options;
let oldValue;
// 1. 初始化:获取初始值
const getInitialValue = () => {
// 深度监听时递归收集对象所有属性依赖
if (deep && isObject(source.value)) {
traverse(source.value);
}
return source.value;
};
// 2. 副作用函数:数据变化时执行
const effectFn = () => {
const newValue = source.value;
callback(newValue, oldValue); // 触发回调
oldValue = newValue;
};
// 3. 立即执行(immediate为true时)
if (immediate) {
oldValue = getInitialValue();
effectFn();
} else {
oldValue = getInitialValue();
}
// 4. 关联响应式数据:依赖变化时触发effectFn
effect(effectFn, { lazy: false });
}
// 实际使用
const user = reactive({ name: '张三', age: 20 });
// 监听user.age,支持深度监听
watch(
() => user.age,
(newAge, oldAge) => {
console.log(`年龄从${oldAge}变为${newAge}`);
},
{ immediate: true, deep: false }
);深度监听(deep: true)通过递归遍历对象属性实现,会收集所有子属性依赖,性能消耗较高,非必要不使用。
四、核心差异与适用场景
| 维度 | computed | watch |
|---|---|---|
| 核心机制 | 依赖推导,缓存结果 | 监听变化,触发副作用 |
| 缓存特性 | 有缓存,依赖不变不计算 | 无缓存,变化即触发 |
| 返回值 | 必须返回值(派生数据) | 无需返回值(执行操作) |
| 适用场景 | 数据格式化、多值计算(如总价=单价×数量) | 异步操作、数据变化后的复杂逻辑(如刷新列表) |
五、实战避坑要点
- computed避免副作用:getter中不可执行异步操作或修改DOM,否则会破坏缓存逻辑和响应式追踪。
- watch精简监听源:尽量监听具体属性(如()=>user.age)而非整个对象,减少深度监听的性能损耗。
- Vue3注意点:watch监听reactive对象时,默认开启深度监听;监听ref时需通过.value访问。
- 缓存失效场景:computed依赖非响应式数据(如普通变量)时,数据变化不会触发重新计算。
到此这篇关于Vue中的computed和watch的原理解析的文章就介绍到这了,更多相关vue computed和watch原理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!


最新评论