一、核心概念:Watch 的作用
Watch 用于监听响应式数据的变化并执行副作用操作。与 Computed 不同,Watch 不返回新值,而是响应变化。
javascript
import { watch, ref } from 'vue'
const count = ref(0)
watch(count, (newVal, oldVal) => {
console.log(`从 ${oldVal} 变为 ${newVal}`)
})
二、Reactive vs Ref:关键差异
1. Reactive 对象:默认深度监听
javascript
import { reactive, watch } from 'vue'
const user = reactive({ name: '张三', age: 20 })
// ✅ 默认深度监听,修改属性会触发
watch(user, () => {
console.log('user变化了') // 修改 user.name 会触发
})
// 等价于显式声明 deep
watch(user, () => {}, { deep: true })
2. Ref 对象:需要显式深度监听
javascript
import { ref, watch } from 'vue'
const user = ref({ name: '张三', age: 20 })
// ❌ 修改属性不会触发(只监听引用变化)
watch(user, () => {
console.log('不会触发') // user.value.name = '李四' 不会触发
})
// ✅ 需要显式声明 deep
watch(user, () => {
console.log('会触发') // 修改属性会触发
}, { deep: true })
三、深度监听的性能问题
深度监听会递归遍历对象的所有属性,性能开销大。
javascript
// ❌ 不推荐:深度监听大型对象
const largeObj = reactive({ /* 大量嵌套数据 */ })
watch(largeObj, callback, { deep: true }) // 性能差
// ✅ 推荐:监听特定属性
watch(() => largeObj.importantProp, callback)
// ✅ 推荐:监听多个特定属性
watch(
() => [largeObj.prop1, largeObj.prop2],
callback
)
四、实用配置选项
1. immediate:立即执行
javascript
const count = ref(0)
watch(count, callback, {
immediate: true // 立即执行一次回调
})
2. flush:控制回调时机
javascript
watch(source, callback, {
flush: 'post' // DOM更新后执行,适合操作DOM
})
五、新旧值的陷阱
深度监听时,新旧值引用相同!
javascript
const obj = reactive({ a: 1 })
watch(obj, (newVal, oldVal) => {
console.log(newVal === oldVal) // true!同一个对象
}, { deep: true })
// 解决方案:手动创建副本
watch(
() => ({ ...obj }),
(newVal, oldVal) => {
console.log(newVal === oldVal) // false
},
{ deep: true }
)
六、最佳实践总结
-
监听选择
-
基本类型:
watch(refValue, callback) -
Reactive对象:
watch(reactiveObj, callback)(默认深度) -
Ref对象:
watch(refObj, callback, { deep: true })
-
-
性能优化
javascript
// ❌ 避免深度监听大型对象 watch(largeObj, callback, { deep: true }) // ✅ 监听特定属性 watch(() => obj.importantField, callback) // ✅ 计算属性+监听组合 const importantData = computed(() => obj.a + obj.b) watch(importantData, callback) -
清理副作用
javascript
const stop = watch(source, callback) // 组件卸载时清理 onUnmounted(() => { stop() }) -
使用 watchEffect(自动依赖追踪)
javascript
watchEffect(() => { // 自动追踪使用的响应式数据 console.log(obj.name, obj.age) })
七、核心要点速记
-
Reactive默认深度,Ref需要显式声明deep
-
深度监听有性能开销,尽量监听特定属性
-
深度监听的新旧值是同一引用
-
合理使用immediate和flush选项
-
记得清理不再需要的监听器
选择正确的监听策略,可以让你的Vue应用既保持响应性,又拥有优秀的性能表现。