刚开始写 Vue3 时,这三个 API 很容易混着用。它们都和响应式数据有关,但处理的问题其实不同:computed 用来推导数据,watch 用来处理明确的变化,watchEffect 适合自动收集依赖的副作用。
先记住这个选择方式
- 页面上需要一个由已有数据计算出来的新值,用
computed。 - 某个值变化后需要请求接口、保存草稿或同步第三方状态,用
watch。 - 一开始不确定依赖有哪些,并且希望代码立即执行一次,用
watchEffect。
不要为了“监听数据”而一律使用 watch。如果只是把数量和单价相乘得到金额,直接计算更清晰。
用 computed 推导页面数据
下面这个小计只依赖数量和单价。它没有请求接口,也没有修改外部状态,因此最适合用 computed。
| |
computed 会缓存结果。只有 count 或 unitPrice 改变时,totalPrice 才会重新计算。模板里可以直接使用 totalPrice,不需要 .value。
用 watch 处理可控的副作用
搜索关键词变化后请求列表,是典型的 watch 场景。它的好处是监听来源明确,也能在新请求开始前清理旧请求。
| |
用户连续输入时,前一次请求可能比后一次更晚返回。上面的 AbortController 会在关键词再次变化时取消旧请求,避免旧结果覆盖新结果。
如果页面打开时也要先加载一次,可以传入第三个配置参数:
| |
用 watchEffect 快速同步依赖
watchEffect 会立即运行,并在执行过程中自动收集它读到的响应式数据。比如把编辑状态保存到本地草稿:
| |
这里不需要手动写 [draft.title, draft.content]。不过依赖来源越隐蔽,后期越难排查,因此涉及请求、路由跳转或复杂业务时,我更倾向于用 watch 写清监听对象。
常见误区
computed 不适合写异步请求。它应该保持纯粹,只返回由现有状态推导出的结果。
watchEffect 不适合替代所有 watch。它会自动追踪执行期间访问过的响应式数据,代码稍复杂时可能会触发比预期更多的更新。
监听对象时也要注意层级。watch(user, callback) 监听 reactive 对象会触发深层变化;如果只关心用户名,就写成 watch(() => user.name, callback),范围更准确。
小结
把三者分开后,代码会更好读:页面展示值交给 computed,明确的业务变化交给 watch,简单的自动同步交给 watchEffect。这也是我在 Vue3 项目里判断响应式逻辑该放在哪里的第一步。