目录
4.监视ref或reactive定义的数据类型中的某一个属性时
watch
基本使用
作用:
监视数据的变化
基本使用:
监视类型只有四种
- 通过ref定义的数据
- 通过reactive定义的数据
- getter函数(函数返回一个值)
- 上述三种情况的数组
/*
监视【ref】定义的【对象类型】数据,监视的是对象的地址值,若想监视对象内部属性的变化,需要手动开启深度监视
watch的第一个参数是:被监视的数据
watch的第二个参数是:监视的回调
watch的第三个参数是:配置对象(deep、immediate等等.....)
*/
watch(test,(newValue,oldValue)=>{
console.log('test变化了',newValue,oldValue)
},{deep:true})
特殊情况
1.监视ref定义的基本数据数据时
监视对象直接写数据名,不用带.value,此时监视的是其value值的变化
2.监视ref定义的对象数据类型时
watch监听ref定义的对象数据类型时,监视的是对象的地址值,若想监视对象内部的数据需要手动开启深度监视
- 若修改的是ref定义的对象的属性,newValue和oldValue都是新值,因为是同一个对象
- 若修改整个ref定义的对象时,newValue是新值,oldValue是旧值,因为不是同一个对象了
3.监视reactive定义的对象数据类型时
监视reactive定义的对象数据类型时,此时默认开启了深度监视
4.监视ref或reactive定义的数据类型中的某一个属性时
-
若该属性不是对象类型,需要写成函数形式
-
若该属性值依然是对象类型,可以直接写,也可以写成函数 () => object.key
5.监视上述多个数据
监视上述多个数据情况下,将多个数据放入数组中进行监视
核心实现逻辑
watch源码执行流程
- 依赖收集:通过traverse递归遍历监视对象(deep:true深度监视时)
- 旧值缓存: 首次执行时缓存初始值
- 调度器触发: 数据变化时通过scheduler调度回调
面试重点
watch的depp实现原理
递归遍历对象属性,通过
Object.keys(value).forEach(k => traverse(value[k]))
强制触发所有层级的getter
如何优化大量watch的性能
- 优先使用
watchEffect
替代多个独立watch
- 合并监听源:
watch([src1, src2], handler)
- 使用
{ flush: 'post' }
延迟到 DOM 更新后执行
watchEffect
基本使用
作用:
立即运行一个函数,自动收集函数内部的所有依赖,同时响应式地追踪其依赖,并在依赖更改时重新执行该函数
核心机制
- 立即执行:初始化时立即运行副作用函数
- 自动依赖追踪:通过
Proxy
的get
陷阱收集依赖 - 清理机制:提供
onInvalidate
处理异步副作用
// watchEffect 核心逻辑
function watchEffect(effect, options) {
const _effect = new ReactiveEffect(effect);
_effect.onTrack = options?.onTrack; // 调试钩子
_effect.run(); // 立即执行
return () => _effect.stop();
}
面试重点
为什么 watchEffect
要返回停止函数
防止组件卸载后副作用继续执行导致内存泄漏,需在 onUnmounted
生命周期调用