应优先用computed处理派生值(如全名、总价),因其自动缓存、惰性求值;需响应变化执行动作(如请求、埋点)时才用watch,因其支持异步、新旧值对比及立即执行。

直接看核心:衍生状态本身该用 computed,衍生之后要“做点事”才轮到 watch。关键不在“要不要响应变化”,而在于你最终需要的是一个值,还是一个动作。
你真正想要的是一个可复用的派生值
比如用户全名(firstName + lastName)、购物车总价、筛选后的商品列表、表单是否有效——这些都属于“基于已有数据推导出的新数据”。它们会被模板多次读取,也可能被其他逻辑引用。
这时用 computed:
- 自动缓存结果,依赖不变时反复读取不重算
- 惰性求值,没人访问就不会执行,节省性能
- 语义清晰,
fullName.value就像一个普通变量,不用管它怎么来的 - 必须是同步函数,不能
await,也不该发请求、改 localStorage
你需要在状态变化后触发外部行为
比如关键词一变就请求搜索接口、价格变动就上报埋点、用户选中某项就滚动到对应区域——这些不是为了产出一个新值,而是为了“响应变化去干件事”。
这时用 watch:
- 每次变化都触发回调,没有缓存干扰
- 天然支持异步,可直接
await fetch()或加防抖 - 能拿到新旧值对比,适合做条件判断(如只在长度超 3 时搜索)
- 支持
immediate: true,创建时立刻执行一次
别混淆的两个典型错误
常见误用会把逻辑搞拧:
- 在
computed里调用api.get()—— 计算属性不该有副作用,且无法等待异步结果 - 用
watch去算总价再赋值给ref—— 绕远路,失去缓存和响应式联动,还容易漏掉依赖追踪
组合使用才是真实项目常态
一个搜索功能往往同时需要两者:
-
filteredItems = computed(() => items.filter(...))—— 派生过滤后列表,供模板直接渲染 -
watch(searchTerm, () => loadMore(), { debounce: 300 })—— 输入变化时加载新数据,不影响已渲染内容
前者保证视图始终显示最新计算结果,后者控制何时发起网络请求,职责分明,互不干扰。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










