应监听 computed 属性而非直接调用——computed 是响应式计算值,watch 其返回值可利用缓存、精准触发副作用;异步依赖下仍有效;对象需 deep: true,否则默认浅监听即可。

直接在 watch 里调用 computed 是无效的——因为 computed 是响应式计算值,不是普通函数,不能“手动触发”。真正要解决“先完成计算、再执行副作用”,关键不是让 watch 去调 computed,而是让 watch 监听 computed 的结果。
监听 computed 的返回值
computed 属性本身是响应式的,它的变化会触发 watch。这是最自然、最符合 Vue 响应式设计的方式。
- 把派生逻辑写在 computed 中(比如过滤列表、拼接状态、判断是否可提交)
- 在 watch 中监听该 computed 属性,一旦它变化,就执行副作用(如发请求、存 localStorage、弹提示)
- 这样既利用了 computed 的缓存和依赖追踪,又让 watch 专注做“动作”
避免重复计算 + 精准触发
如果直接 watch 原始数据(比如 form.name 和 form.email),每次修改都会触发;而改成 watch 一个 computed(如 isFormValid),只有当有效性真正切换时才执行,更语义化、更高效。
- ✅ 推荐写法:
watch(() => isFormValid.value, (newVal) => { if (newVal) submitForm(); }) - ❌ 不推荐:
watch([() => form.name, () => form.email], () => { ... })—— 逻辑分散、无缓存、易误触发
处理异步依赖场景
当 computed 依赖的数据本身是异步更新的(比如从 API 获取后赋值给 ref),watch 仍能正确响应——只要 computed 的依赖项是响应式的,它的值变化就会被捕捉。
- 例如:
userInfo是一个 ref,由fetchUser()异步填充;displayName = computed(() => userInfo.value?.name || '游客') - watch
displayName,就能在用户信息加载完成的那一刻,精准执行后续动作(如初始化第三方 SDK)
注意 watch 的时机与深度
watch 默认是浅层监听。如果 computed 返回的是对象或数组,且你关心其内部字段变化,需显式开启 deep: true;但多数情况下,你只关心“这个计算结果是否变了”,用默认浅监听即可。
- 简单布尔/字符串/数字:无需
deep - 需要对比对象内容(如
computed(() => ({ a: x, b: y }))):加{ deep: true } - 更优替代:改用
watchEffect自动追踪内部访问,但 watch computed 更明确、更可控

















