Vue3 watch 与 watchEffect 深度解析
一、响应式监听的基石作用
在Vue3的响应式系统中,watch
和watchEffect
是构建复杂状态逻辑的关键工具。它们实现了对响应式数据的精准监听,支撑着现代前端开发中的状态管理、副作用处理等核心功能。
1.1 演变
- Options API时代:通过watch选项配置
- Composition API革命:函数式监听
- 响应式系统升级:基于Proxy的全新实现
二、watch 深度解析
2.1 核心语法
watch(source, callback, options?)
2.2 参数详解
// 监听单个ref
const count = ref(0)
watch(count, (newVal, oldVal) => {
console.log(`Count changed from ${
oldVal} to ${
newVal}`)
})
// 监听getter函数
watch(
() => state.user.age,
(age) => console.log('Age updated:', age)
)
// 监听多个源
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
// 处理变化逻辑
})
2.3 配置选项
选项 | 类型 | 默认值 | 说明 |
---|---|---|---|
deep | boolean | false | 深度监听对象变化 |
immediate | boolean | false | 立即执行回调 |
flush | string | ‘pre’ | 回调执行时机(pre/post/sync) |
onTrack | function | - | 依赖追踪时触发 |
onTrigger | function | - | 依赖变更时触发 |
三、watchEffect 全面剖析
3.1 基本用法
const stop = watchEffect((onCleanup) => {
console.log('Count:', count.value)
onCleanup(() => {
console.log('Cleanup previous effect')
})
})
// 停止监听
stop()
3.2 特性解析
- 自动依赖收集
- 立即执行特性
- 清理机制
- 异步处理支持
四、核心原理揭秘
4.1 底层架构
// 核心effect实现
class ReactiveEffect {
constructor(fn, scheduler) {
this.fn = fn
this.scheduler = scheduler
// ...
}
run() {
// 依赖收集逻辑
activeEffect = this
return this.fn()
}
}
4.2 watch实现流程
4.3 watchEffect特殊处理
- 立即执行fn函数
- 自动追踪所有访问的响应式属性
- 无旧值处理逻辑
五、对比分析表
特性 | watch | watchEffect |
---|---|---|
执行时机 | 默认延迟 | 立即执行 |
依赖收集 | 显式声明 | 自动追踪 |
返回值处理 | 新旧值对比 | 无旧值获取 |
使用场景 | 精确监听特定数据 | 副作用聚合管理 |
性能消耗 | 较低(精确监听) | 较高(自动追踪) |
清理机制 | 回调参数中处理 | 通过onCleanup注册 |
多源监听 | 支持数组形式 | 不支持 |
初始执行 | 需要immediate选项 | 默认执行 |
六、高级使用技巧
6.1 性能优化方案
// 合理使用flush配置
watch(
data,
() => {
// DOM更新后执行
},
{
flush: 'post' }
)
// 防抖处理示例
let timeout
watch(searchQuery, (value) => {
clearTimeout(timeout)
timeout = setTimeout(() => {
// 执行搜索
}, 500)
})
6.2 组合式应用
// 组合监听示例
function useSmartWatcher(source, fn) {
const isPending = ref(false)
watch(source, async (newVal) => {
isPending.value = true
try {
await fn(newVal)
} finally {
isPending.value = false
}
})
return {
isPending }
}
6.3 调试技巧
watchEffect(
(effect) => {
// 业务逻辑
},
{
onTrack(e) {
debugger
console.log('Track:', e)
},
onTrigger(e) {
console.log('Trigger:', e)
}
}
)
七、源码关键逻辑解析
7.1 watch初始化
function watch(source, cb, options) {
// 处理source标准化
const getter = isMultiSource
? () => source.map(s => traverse(s))
: typeof source === 'function'
? () => source()
: () => traverse(source)
// 创建effect
const effect = new ReactiveEffect(getter, scheduler)
// 立即执行逻辑处理
if (options.immediate) {
job()
}
}
7.2 依赖更新处理
const scheduler = () => {
// 获取新值
const newValue = effect.run()
// 执行清理函数
cleanup()
// 触发回调
cb(newValue, oldValue, onCleanup)
// 更新旧值
oldValue = newValue
}
八、常见问题解决方案
8.1 无限循环问题
watch(data, () => {
// 避免在回调中修改被监听的数据
data.value = newValue // 危险操作!
})
// 正确做法:使用条件判断
watch(data, (newVal) => {
if (needUpdate) {
data.value = calculatedValue
}
})
8.2 异步处理规范
watchEffect(async (onCleanup) => {
const ignore = {
current: false }
onCleanup(() => {
ignore.current = true })
const data = await fetchData()
if (!ignore.current) {
// 处理数据
}
})
九、最佳实践总结
- 明确监听目标:优先使用watch进行精确监听
- 合理使用深度监听:避免不必要的性能消耗
- 注意内存泄漏:及时清理不需要的监听
- 组合API使用:结合computed等响应式API
- 性能敏感场景:合理使用flush配置
结语:watch和watchEffect是Vue3响应式系统的核心武器,理解其实现原理和适用场景,能够帮助我们构建更高效、更健壮的Vue应用。根据具体场景选择合适的监听方式,结合良好的编程实践,将大幅提升应用性能和开发体验。