在 Vue 里,computed 和 watch 经常被放在一起比较。
很多人刚开始学的时候会有一个疑问:
两者都能监听数据变化,那到底什么时候用 computed,什么时候用 watch?
这篇文章直接从实战角度说清楚:
watch是干什么的watch和computed的核心区别watch适合哪些业务场景- Vue2 和 Vue3 里
watch的写法有什么不同 - 实战中怎么选
一、先说结论
一句话先记住:
computed适合做派生值watch适合做响应副作用
什么叫副作用?
比如:
- 数据变了,发请求
- 某个字段变了,联动重置另一个字段
- 路由参数变了,重新加载列表
- 搜索条件变了,触发查询
- 一个开关变化后,执行某些业务逻辑
这些都更像 watch 的工作。
二、watch 是什么
watch 是 Vue 提供的侦听器,它会监听某个响应式数据的变化,并在变化时执行回调。
比如:
js
watch: {
keyword(newVal, oldVal) {
console.log('keyword变了', newVal, oldVal)
}
}
只要 keyword 改变,就会触发这个函数。
三、watch 的核心特点
1. 它关注"变化"
watch 的核心不是"返回一个值",而是"监听变化后做事"。
2. 它适合副作用
比如请求接口、写入缓存、联动修改其他值、控制状态等。
3. 它可以拿到新值和旧值
这是 watch 很实用的一点:
js
watch: {
status(newVal, oldVal) {
console.log(newVal, oldVal)
}
}
你可以很方便地做前后值对比。
4. 它支持深度监听、立即执行
这些都是 computed 没有的典型能力。
四、watch 和 computed 的区别
1. computed 是"算结果",watch 是"做动作"
computed:
js
computed: {
fullName() {
return this.firstName + ' ' + this.lastName
}
}
它输出一个新值。
watch:
js
watch: {
fullName(newVal) {
console.log('名字变了,做点事')
}
}
它不负责返回结果,而是负责执行逻辑。
2. computed 有缓存,watch 没有缓存概念
computed 会缓存结果,依赖不变就不重新计算。
watch 不一样,它是只要侦听值变化,就触发回调。
3. computed 适合模板展示,watch 适合业务联动
比如:
- 页面上要显示"是否可编辑" -> computed
- 选中某项后自动拉接口 -> watch
4. computed 更像"属性",watch 更像"事件"
computed 你会像用字段一样用它。
watch 更像是"这个值一变,就触发一个动作"。
五、watch 的典型使用场景
场景 1:搜索框输入后触发查询
js
watch: {
keyword(newVal) {
this.pageNum = 1
this.getList()
}
}
当关键词变化时,自动查询列表。
场景 2:联动重置表单
js
watch: {
areaCode(newVal) {
this.formData.streetCode = ''
}
}
当区县变化时,重置街道。
这种联动在后台管理系统里特别常见。
场景 3:路由变化后重新加载数据
js
watch: {
'$route.query.id'(newVal) {
this.getDetail(newVal)
}
}
路由参数一变,重新请求详情。
场景 4:监听对象内部字段变化
js
watch: {
formData: {
handler(newVal) {
console.log('表单变了')
},
deep: true
}
}
如果是对象或数组内部变化,常常需要 deep: true。
六、watch 的常见写法
1. Vue2 选项式写法
这是最经典的写法:
js
export default {
data() {
return {
keyword: ''
}
},
watch: {
keyword(newVal, oldVal) {
console.log(newVal, oldVal)
}
}
}
2. 对象形式写法
适合复杂监听:
js
watch: {
keyword: {
handler(newVal, oldVal) {
console.log(newVal, oldVal)
},
deep: true,
immediate: true
}
}
3. 深度监听
js
watch: {
formData: {
handler(newVal) {
console.log('formData changed')
},
deep: true
}
}
适合监听对象内部属性变化。
4. 立即执行
js
watch: {
keyword: {
handler(newVal) {
this.getList()
},
immediate: true
}
}
immediate: true 会让监听器在初始化时也执行一次。
七、Vue2 里的 watch
Vue2 中,watch 基本写在组件的 watch 选项里。
基础写法
js
watch: {
count(newVal) {
console.log(newVal)
}
}
带 immediate
js
watch: {
count: {
handler(newVal) {
console.log(newVal)
},
immediate: true
}
}
带 deep
js
watch: {
obj: {
handler() {
console.log('obj changed')
},
deep: true
}
}
监听路径
js
watch: {
'formData.areaCode'(newVal) {
console.log(newVal)
}
}
Vue2 的 watch 很适合传统后台项目,尤其是表单联动比较多的场景。
八、Vue3 里的 watch
Vue3 里,watch 主要和 ref、reactive 一起用,在组合式 API 中更灵活。
1. 监听 ref
js
import { ref, watch } from 'vue'
const keyword = ref('')
watch(keyword, (newVal, oldVal) => {
console.log(newVal, oldVal)
})
注意:
- 监听的是
ref本身 - 不需要写
.value当作监听源
2. 监听多个数据
js
watch([keyword, pageNum], ([newKeyword, newPage]) => {
console.log(newKeyword, newPage)
})
这个在查询条件联动时非常实用。
3. 监听 reactive 对象
js
import { reactive, watch } from 'vue'
const formData = reactive({
areaCode: '',
streetCode: ''
})
watch(
() => formData.areaCode,
(newVal, oldVal) => {
console.log(newVal, oldVal)
}
)
Vue3 中监听 reactive 的属性,通常用 getter 函数写法更稳。
4. 深度监听
Vue3 中对 reactive 对象的监听,本身就更贴近响应式系统,但如果你要监听对象内部整体变化,仍然可能用到 deep: true。
js
watch(
formData,
() => {
console.log('formData changed')
},
{ deep: true }
)
5. 立即执行
js
watch(
keyword,
(newVal) => {
console.log(newVal)
},
{ immediate: true }
)
这个和 Vue2 的 immediate 很类似。
九、Vue2 和 Vue3 的 watch 对比
1. 写法不同
Vue2:
js
watch: {
keyword(newVal) {
console.log(newVal)
}
}
Vue3:
js
watch(keyword, (newVal) => {
console.log(newVal)
})
2. 响应式来源不同
Vue2 多用 data,通过 this.xxx 访问。
Vue3 常用:
refreactivecomputed
所以 Vue3 的 watch 更偏函数式写法。
3. 组合能力更强
Vue3 可以很自然地监听多个源:
js
watch([keyword, pageNum], ([k, p]) => {
console.log(k, p)
})
这在复杂业务里比 Vue2 更灵活。
4. 逻辑更容易拆分
Vue3 里 watch 可以和业务状态放在一起写,更适合组合式组织。
十、watch 的实战写法建议
建议 1:不要滥用 deep
deep: true 很方便,但也更耗性能。
如果只关心某个字段,尽量监听单个字段,不要整对象深度监听。
坏例子:
js
watch: {
formData: {
handler() {},
deep: true
}
}
如果只是监听 areaCode,那就直接监听它。
建议 2:能监听具体字段就不要监听整个对象
比如:
js
watch: {
'formData.areaCode'(newVal) {
this.formData.streetCode = ''
}
}
比深度监听整个表单更清晰。
建议 3:watch 里尽量只做"响应动作"
比如:
- 请求接口
- 重置字段
- 切换状态
- 派发事件
不要在 watch 里塞太多复杂计算,复杂派生值尽量交给 computed。
建议 4:初始化时要分清要不要 immediate
有些场景你希望页面一加载就执行一次,比如:
- 首次查询
- 初始化联动数据
这时用 immediate: true 很合适。
但如果你不想初始就触发,记得不要加。
十一、一个典型的后台实战例子
比如数据分析页面:
- 选择区县后,街道要重置
- 选择年份后,自动刷新列表
- 选择角色后,显示字段要变化
可以这么做:
js
watch: {
'formData.areaCode'(newVal) {
this.formData.streetCode = ''
},
'formData.year'() {
this.getList()
}
}
这里用 watch 就非常自然:
- 区县变了,联动清街道
- 年份变了,重新查数据
而像"当前是否显示某列"这种派生判断,更适合 computed。
十二、watch 和 computed 怎么选
选 computed,如果:
- 你要的是一个"计算结果"
- 结果会被模板多次使用
- 结果可以缓存
- 没有副作用
例如:
fullNameshowColumncurrentAreaCodetableTitle
选 watch,如果:
- 你要监听变化后做事
- 需要发请求
- 需要联动修改其他状态
- 需要拿到新旧值
例如:
- 搜索条件变化自动查询
- 区县变化清空街道
- 路由变化重新拉数据
- 开关变化后执行某逻辑
十三、最后总结
watch 的本质不是"计算",而是"响应变化"。
它和 computed 的分工很明确:
computed负责派生值watch负责副作用处理
你可以这样记:
- 要展示结果,用
computed - 要监听变化做事,用
watch
Vue2 / Vue3 的区别也很清楚:
- Vue2 更偏选项式写法
- Vue3 更偏组合式写法
- Vue3 的
watch更灵活,适合复杂业务拆分