Vue.js 作为现代前端框架,其响应式系统让数据驱动视图变得简单。computed 和 watch 是处理数据变化的两大核心手段,虽然都能监听变化,但设计初衷和使用场景截然不同。
computed:缓存的派生状态
computed 的核心在于依赖缓存。它会根据响应式依赖自动追踪变化,只有当依赖项发生改变时才会重新计算。如果依赖没变,多次访问计算属性会直接返回缓存结果,性能开销极小。
这决定了它最适合用于模板中的复杂计算或派生状态。比如根据多个输入字段生成一个展示值,或者对列表进行过滤排序。
new Vue({
data: { a: 1, b: 2 },
computed: {
sum() {
return this.a + this.b;
}
}
});
上面的例子中,只要 a 或 b 不变,sum 就不会重复执行函数体。这种特性让它在渲染层非常高效。
watch:监听副作用与异步操作
相比之下,watch 更像是一个观察者模式实现。它不关心返回值,只关注数据变化后需要执行的副作用。比如发起网络请求、操作 DOM、或者执行耗时较长的逻辑。
由于没有缓存机制,每次触发都会运行回调,因此不适合做纯计算逻辑。
new Vue({
data: { question: '' },
watch: {
question(newQuestion) {
// 模拟防抖后的异步请求
this.getAnswer();
}
},
methods: {
getAnswer() {
console.log('Fetching answer...');
}
}
});
在这个场景中,我们利用 watch 监听输入框变化,并在用户停止输入后触发查询。如果需要深度监听对象内部变化,还可以配合 deep 配置项使用。
实战示例:组合使用
在实际开发中,两者往往配合使用。下面这个组件展示了如何在一个页面里同时利用 computed 做实时计算,用 watch 处理按钮触发的逻辑。
<template>
<div>
<input v-model="number" type="number" />
<p>原始数字:{{ number }}</p>
<!-- computed 自动更新 -->
<p>数字的平方(computed):{{ squaredNumber }}</p>
<button @click="findSquareRoot">计算平方根(watch 触发)</button>
<!-- watch 手动触发 -->
<p>数字的平方根(watch):{{ squareRoot }}</p>
</div>
</template>
<script>
export default {
data() {
return {
number: 0,
squareRoot: 0
};
},
computed: {
squaredNumber() {
return this.number * this.number;
}
},
watch: {
number(newValue) {
// 监听变化,但不立即计算,而是等待用户点击或其他逻辑
// 这里仅演示监听器存在
}
},
methods: {
findSquareRoot() {
this.squareRoot = Math.sqrt(this.number);
}
}
};
</script>
最佳实践建议
- 优先选择 computed:只要涉及模板展示的计算,且不需要副作用,尽量用
computed。它的缓存机制能避免不必要的重渲染。 - 慎用复杂逻辑:不要在
computed里写复杂的业务逻辑或异步请求,保持其'纯函数'的特性。 - 善用 watch 配置:
immediate可以在初始化时立即执行,deep可以监听对象深层变化,合理搭配能解决很多边界问题。 - 明确职责:计算状态归
computed,处理副作用(如 API 调用、DOM 操作)归watch。
理解这两者的差异,能帮助我们在设计 Vue 应用时做出更合理的架构决策,既保证性能又提升可维护性。


