前言
vue3中, watch使用 watch(()=>xxx,v=>{})或者watch(xxx,val=>{})时, 总是遇到函数写法的watch监听,数据改变后没有监听到数据改变;非函数的写法反而可以监听到。
在 Vue 3 中,这两种 watch 的使用方式确实有重要区别:
1. 语法区别
javascript
// 方式一:使用函数
watch(() => obj.property, (val) => {
console.log('值变化了:', val)
})
// 方式二:直接监听响应式对象
watch(obj, (val) => {
console.log('对象变化了:', val)
})
2. 关键区别
方式一: 函数 () => xxx
- 监听特定属性 :只追踪
obj.property这个具体的值 - 浅层监听 :只有当
obj.property的值本身发生变化时才触发 - 不追踪对象引用 :不关心整个
obj对象的变化
方式二:直接监听响应式对象 xxx
- 监听整个对象:追踪对象的所有属性变化
- 深层监听:默认深度监听对象内部所有嵌套属性的变化
- 追踪引用变化:也会监听对象本身的重新赋值
3. 为什么有时候 "函数的写法监听不到?"
情况一:嵌套对象属性变化
javascript
const obj = reactive({
nested: {
value: 1
}
})
// ❌ 监听不到 nested.value 的变化
watch(() => obj.nested, (val) => {
console.log('不会触发')
})
// ✅ 需要深度监听
watch(() => obj.nested, (val) => {
console.log('会触发')
}, { deep: true })
// ✅ 或监听具体属性
watch(() => obj.nested.value, (val) => {
console.log('会触发')
})
情况二:数组操作
javascript
const arr = reactive([1, 2, 3])
// ❌ 监听不到 push/pop 等操作
watch(() => arr.length, (val) => {
console.log('可能不会触发')
})
// ✅ 直接监听数组
watch(arr, (val) => {
console.log('会触发')
})
情况三:解构丢失响应性
javascript
const obj = reactive({ x: 1, y: 2 })
// ❌ 解构会丢失响应性
const { x } = obj
watch(x, (val) => { }) // 无效
// ✅ 使用 getter 函数
watch(() => obj.x, (val) => {
console.log('会触发')
})
4. 实践建议
使用函数 () => xxx 的场景:
- 监听基本类型值(string, number, boolean)
- 监听对象的特定属性
- 需要计算或组合的依赖
- 避免不必要的深度监听
javascript
// 监听特定属性
watch(() => user.name, (name) => {
console.log('用户名变化:', name)
})
// 监听计算值
watch(() => x.value + y.value, (sum) => {
console.log('和变化:', sum)
})
使用直接监听 xxx 的场景:
- 需要监听对象所有变化
- 监听数组变化
- 需要深度监听嵌套对象
- 监听引用变化
javascript
// 监听整个响应式对象
watch(state, (newState) => {
console.log('状态变化:', newState)
}, { deep: true })
// 监听数组
watch(items, (newItems) => {
console.log('列表变化:', newItems)
})
5. 注意事项
ref 对象的处理:
javascript
const count = ref(0)
// ✅ 正确:直接监听 ref
watch(count, (val) => {
console.log('count变化:', val)
})
// ✅ 也正确:通过 .value 监听
watch(() => count.value, (val) => {
console.log('count变化:', val)
})
reactive 对象的处理:
javascript
const state = reactive({ count: 0 })
// ✅ 推荐:使用 getter 监听特定属性
watch(() => state.count, (val) => {
console.log('count变化:', val)
})
// ⚠️ 注意:直接监听 reactive 对象会自动开启深度监听
watch(state, (val) => {
console.log('state变化:', val) // 任何嵌套属性变化都会触发
})
总结
选择哪种方式主要取决于:
- 监听粒度:需要监听整个对象还是特定属性
- 性能考虑:避免不必要的深度监听
- 数据类型:基本类型用 getter,复杂对象可直接监听
- 变化类型:需要监听引用变化还是值变化
当发现监听不到变化时,检查是否是:
- 嵌套对象属性变化(需要
deep: true) - 数组方法修改(直接监听数组)
- 解构导致响应性丢失(使用函数)