选 computed 还是 watch,关键看你要“得到一个值”还是“做一件事”:前者用 computed,后者用 watch;computed 适合派生新数据并缓存,watch 适合执行异步或副作用操作。

选 computed 还是 watch,关键看你要“得到一个值”还是“做一件事”。前者用 computed,后者用 watch。
需要派生新数据、直接用于模板时,用 computed
计算属性本质是声明式的数据加工——输入响应式数据,输出一个可响应、可缓存的新值。它天然适合模板中频繁读取、但依赖不常变的场景。
- 全名拼接:
computed(() => `${firstName.value} ${lastName.value}`),模板里直接写{{ fullName }} - 购物车总价:
computed(() => cartItems.reduce((sum, item) => sum + item.price * item.count, 0)),数值变化自动更新,且多次访问不重复计算 - 列表过滤:
computed(() => list.filter(item => item.status === 'active')),结果可直接遍历渲染
需要执行副作用、涉及异步或外部操作时,用 watch
侦听器是命令式的响应机制,核心任务不是返回值,而是“数据一变就干活”。它支持异步、能访问新旧值、可深度监听、还能控制触发时机。
- 搜索框防抖请求:
watch(searchTerm, (newVal) => { if (newVal.length > 2) debounceFetch(newVal) }, { immediate: false }) - 表单联动提交:
watch(() => [form.name, form.email], () => saveToLocalStorage(form), { deep: true }) - 路由参数变化后重载数据:
watch(() => route.params.id, (id) => fetchUser(id))
依赖多个响应式源且逻辑简单,优先 computed;逻辑复杂或需控制执行节奏,选 watch
比如两个输入框联动生成提示文案,若只是拼接字符串,用 computed 更简洁;但若要根据组合结果调用接口、弹窗、发日志,就必须用 watch。
- ✅ 简单组合:
computed(() => `当前状态:${status.value},版本:${version.value}`) - ❌ 不适合异步:
computed(() => api.getUser(id.value))—— 会阻塞渲染、破坏缓存、无法处理 loading/error - ✅ 正确做法:
watch(id, (newId) => { loading.value = true; api.getUser(newId).then(...).finally(() => loading.value = false) })
注意几个典型误用点
有些情况看似能用 computed 实现,但实际违背设计原则,容易埋坑:
- 在 computed 里发请求、改 DOM、调用
router.push—— 这些属于副作用,应交给 watch - 监听对象却没加
deep: true,导致嵌套属性变化不触发 —— watch 默认只监听引用,computed 则自动深层追踪 - 需要对比新旧值(如判断是否从 0 变为 1)—— watch 回调提供
(newVal, oldVal),computed 没有旧值概念










