本节摘要:计算属性(computed)缓存派生值,侦听器(watch)在数据变化时执行副作用。前者的价值是"按需重算 + 缓存",后者是"变化即响应"。两者分工稍异,本节把"什么时候用谁"讲清,避免新手见因果就用 watch 的常见绕路。
本节阅读目标
阅读完本节,你应当能够:
常常遇到这种需求:显示某数组的总价、过滤后的列表。这些值都能由现有数据推出来——派生值。最坏的做法是每次渲染都人工算,或在方法里重算。Vue 用 computed 专管这类"由数据派生、还会被复用"的值。
<script setup> import { ref, computed } from 'vue'; const cart = ref([{ price: 20 }, { price: 35 }]); const total = computed(() => cart.value.reduce((s, i) => s + i.price, 0)); </script> <template>合计:{{ total }}</template>
computed 只在它依赖的数据变化时才重算,不依赖的数据变了,它会直接返回缓存。这让派生值既准确又省算力。
computed 管"派生计算",watch 管"变化后的副作用"——切入点是"数据变了,我要去做什么"(比如发请求、写存储、更新别的 UI)。区别在于 watch 的落脚点是动作而不是返回值。
<script setup> import { ref, watch } from 'vue'; const key = ref(''); watch(key, (newVal, oldVal) => { console.log('关键词从', oldVal, '变成', newVal); }); </script>
当"别的状态需要跟着动"时,watch 是明确工具;而"只要显示派生结果"应优先 computed。
新手最常见的绕路是"看到要随输入变化的东西,就条件反射写 watch"。下面这个场景最能说明差别:一个搜索框,下面要实时显示"匹配条数",同时希望在关键词变化时调一次后端接口做联想。
"匹配条数"是典型派生值,用 computed 最合适——它只取决于当前列表,被模板直接引用,还能享受缓存:
<script setup> import { ref, computed } from 'vue'; const kw = ref(''); const all = ref(['林', '月', '风', '云']); const matched = computed(() => kw.value ? all.value.filter((x) => x.includes(kw.value)) : all.value ); </script> <template> <p>匹配 {{ matched.length }} 条</p> </template>
而"触发一次联想请求"是副作用,必须用 watch——因为它的落点是"去请求",不是"返回一个值"。要是你用 computed 去做请求,computed 的缓存与"仅在渲染被读时才求值"的特性,会让这个请求时机变得不可预测、还为复用它的组件带来实打实的隐形重复调用。判据始终是那条:取一个值用 computed,做一件事用 watch。
| 需求 | 该用什么 |
|---|---|
| 我的数据派生一个值给界面显示 | computed |
| 数据从几个源拼出一个新值 | computed |
| 数据变了我要发起请求 / 写存储 / 改别的 | watch |
| 我要监听旧值新值对比后决定 | watch |
通俗记忆:要算就用 computed,要动就用 watch。拿不准先问"我想取一个值,还是想干一件事"。

真实组件里,computed 与 watch 经常同框出现,判据依然管用,但要再明一层"谁先谁后、谁依赖谁"。仍以上面的搜索框为例:matched 是一个 computed——它只读当前列表与关键词,自给自足,谁引用它显示结果;而"关键词一变就去请求联想"则是一个 watch——它的触发依赖 kw 变化,落点是执行异步请求,不是算出值。
把它们摆到一起,最该避免的是"用 computed 递数据、用 watch 重新算同一份值"这种重复劳动。正确分工是:**
凡是"由现有数据能直接推出来"的东西,都交给 computed,watch 越少背"翻译数据为界面"的锅,副作用就越聚焦、越可控。反之,如果某处用了 watch 还把派生结果再塞回一个 state 去显示,那通常意味着这一步本该是 computed 的差事。基准还是那句算与动的分别——只是当两者都在时,把它并列看,能让边界一眼更清。
数据与派生都讲完,Vue 入门线的最后一块是事件与表单。v-model 双向绑定怎么处理用户输入,下一节收官本章。