一分钟先看结论(新手速读)
watch(谁, 回调, 配置):监听“谁”的变化,一变就执行回调。- 监听
ref的基本类型(如数字、字符串)最简单,oldValue/newValue很直观。 - 监听
ref包裹的对象时,想追踪内部属性变化通常要加deep: true。 - 监听
reactive对象时,默认就是深度监听(能感知内部属性变化)。 - 监听“某个属性”时,推荐写成函数:
watch(() => obj.xxx, cb)。 - 需要同时监听多个值时,用数组:
watch([a, b, () => c.d], cb)。
1. 基础语法:watch 长什么样
watch(
source, // 被监听的数据源
(newValue, oldValue) => {
// 变化后执行
},
options, // 可选:deep、immediate 等
)
常见 source 写法:
ref:watch(count, cb)- getter 函数:
watch(() => state.name, cb) - 多数据源数组:
watch([count, () => state.name], cb)
2. 用法 1:监听 ref 的基本类型
import { ref, watch } from 'vue'
const sum = ref(0)
const stopWatch = watch(sum, (newValue, oldValue) => {
console.log('sum 变了', oldValue, '->', newValue)
if (newValue >= 10) {
stopWatch() // 手动停止监听
}
})
要点:
- 基本类型变化时,
oldValue/newValue很好理解,就是前后两个值。 watch的返回值是“停止函数”,在不需要监听时可以手动调用。
3. 用法 2:监听 ref 的对象类型
import { ref, watch } from 'vue'
const person = ref({ name: 'Alice', age: 30 })
watch(
person,
(newValue, oldValue) => {
console.log('person 变了', oldValue, '->', newValue)
},
{ deep: true, immediate: true },
)
要点:
person.value.age++这种“改内部属性”属于对象内部变化,通常要deep: true才能稳定监听。immediate: true会在注册后立刻执行一次回调;第一次通常oldValue是undefined。
4. 用法 3:监听 reactive 对象
import { reactive, watch } from 'vue'
const address = reactive({
city: 'New York',
zipcode: '10001',
})
watch(address, (newValue, oldValue) => {
console.log('address 变了', oldValue, '->', newValue)
})
要点:
- 直接监听
reactive对象时,默认是深度监听。 - 所以改
address.city、address.zipcode都会触发回调。
5. 用法 4:监听对象里的某个属性(重点)
import { reactive, watch } from 'vue'
const company = reactive({
name: 'Google',
location: 'Mountain View',
car: { c1: 'BMW', c2: 'Audi' },
})
watch(
() => company.name,
(newValue, oldValue) => {
console.log('company.name 变了', oldValue, '->', newValue)
},
)
watch(
() => company.car,
(newValue, oldValue) => {
console.log('company.car 变了', oldValue, '->', newValue)
},
{ deep: true },
)
要点(记这条最实用):
- 监听“非对象属性”(如
name)时,必须写 getter:() => company.name。 - 监听“对象属性”(如
car)建议也统一写 getter,心智负担更低,团队代码更一致。 - 监听对象属性内部字段(如
car.c1)时,再配合deep: true。
6. 用法 5:同时监听多个数据源
watch(
[sum, () => person.value.age, () => company.name],
(newValues, oldValues) => {
console.log('多个数据变了', oldValues, '->', newValues)
},
)
要点:
- 把多个 source 放进数组即可。
- 回调参数会变成数组,顺序与 source 数组一致。
7. 最容易踩坑:为什么 oldValue 和 newValue 看起来一样
当监听对象(尤其是深度监听)时,如果只是改了对象内部属性,oldValue 和 newValue 可能是同一个引用。
例如:
person.value.age += 1:对象没换,只是内部改了,可能看到“新旧值长得一样”。person.value = { ... }:整个对象被替换,这时新旧引用才明显不同。
这不是 watch 失效,而是对象引用特性导致的正常现象。
8. 实战建议(给初学者)
- 优先问自己:我监听的是“整个值”,还是“某个属性”?
- 监听属性时优先用 getter 写法:
() => xxx。 - 对对象内部变化有需求时,再加
deep: true,不要无脑全加。 - 初次加载就要执行逻辑时,用
immediate: true。 - 在条件满足后及时调用停止函数,避免无意义监听。
9. 记忆口诀
基本类型看前后;对象内部要深搜;
监听属性用函数;多个来源放数组;
立刻执行加 immediate;不想再听就 stop。
评论区