Vue 里 computed 和 watch 到底怎么选:别把两种能力写成一回事
刚开始用 Vue 时,很多人都会把 computed 和 watch 混着用。
表面上看,它们都能“跟着数据变化做点事”,写起来也都不难。可项目一大,差别就会越来越明显。该放在 computed 里的逻辑写进了 watch,后面会越来越绕;反过来,把副作用塞进 computed,代码也会慢慢变得不好维护。
这两个能力看着相近,职责其实完全不同。
先说最短的结论
如果你只是想基于已有状态推导出一个新值,用 computed。
如果你是想在某个值变化后执行副作用,比如发请求、写本地存储、调第三方接口,用 watch。
这个边界一旦立住,很多代码会立刻顺起来。
computed 更像“结果”
computed 的核心是派生状态。
例如购物车里你已经有:
- 商品列表
- 数量
- 单价
那总价就很适合放进 computed。
1computed: { 2 totalPrice() { 3 return this.items.reduce((sum, item) => { 4 return sum + item.price * item.count; 5 }, 0); 6 } 7}
这个值不需要手动维护,也不需要额外存一份。只要原始数据变了,结果自然就变。
这就是 computed 最舒服的地方:它让状态表达更完整,而不是让你再多维护一份变量。
watch 更像“反应”
watch 则不是拿来推导结果的,它更像对变化的响应。
比如用户切换搜索条件后,你要重新拉一遍列表:
1watch: { 2 keyword(newValue) { 3 this.fetchList(newValue); 4 } 5}
这里真正有意义的不是“生成了一个新值”,而是“变化发生后,要去做一件额外的事”。
请求、日志、埋点、缓存同步,这些都属于 watch 更擅长的范围。
为什么很多代码会把它们写反
因为 watch 看起来什么都能做。
你完全可以监听某个字段变化,再手动去改另一个字段,于是代码慢慢变成这样:
1data() { 2 return { 3 firstName: 'Su', 4 lastName: 'Jf', 5 fullName: '' 6 }; 7}, 8watch: { 9 firstName() { 10 this.fullName = this.firstName + ' ' + this.lastName; 11 }, 12 lastName() { 13 this.fullName = this.firstName + ' ' + this.lastName; 14 } 15}
功能没问题,但结构已经开始别扭了。
因为 fullName 并不是独立状态,它只是另外两个值的组合。既然只是组合,就不该手动维护。
这种写法改成 computed 会更自然,也更不容易漏掉边界情况。
一个简单判断办法
可以问自己一句话:
“这个值是要算出来,还是要顺手做点别的?”
如果答案是“算出来”,优先 computed。
如果答案是“要顺手去做请求、写缓存、触发外部逻辑”,那就是 watch。
别让 watch 变成补丁桶
项目里有个很常见的现象:一开始只是监听一个字段,后面慢慢加条件、加分支、加节流、加异步请求,最后一个 watch 变成了功能中心。
这并不是 watch 的错,而是它太容易被拿来打补丁。
所以在写 watch 时最好克制一点,只让它做“变化后的动作”,不要拿它承担状态建模。
写在最后
computed 和 watch 都很重要,但它们处理的问题不是一类。
一个是“结果”,一个是“反应”。当你不再把它们混着用,Vue 代码会轻很多,状态关系也会更清楚。