当在数据变化后,我们想要去执行一系列副作用时,会选择使用watch函数
watch(() => obj.a, (newVal, oldVal) => {
// 执行一系列操作
}, {deep: true} )
但是深层监听时候,会对监听对象做深度遍历,所以当对象的属性繁多且复杂时,开销较大,降低性能。
具体监听策略通过阅读源码,具体如下所示
源码分析:
主要针对watch中的getter函数进行剖析,当接收到deep为深度监听时,对监听对象做了traverse深度优先遍历
深度优先遍历算法如下所示,
function traverse(value, depth = Infinity, seen) {
// 递归出口
if (depth <= 0 || !shared.isObject(value) || value["__v_skip"]) {
return value;
}
seen = seen || /* @__PURE__ */ new Set();
if (seen.has(value)) {
return value;
}
seen.add(value);
depth--;
// 针对每个数据类型做不同遍历处理
if (reactivity.isRef(value)) {
traverse(value.value, depth, seen);
} else if (shared.isArray(value)) {
for (let i = 0; i < value.length; i++) {
traverse(value[i], depth, seen);
}
} else if (shared.isSet(value) || shared.isMap(value)) {
value.forEach((v) => {
traverse(v, depth, seen);
});
} else if (shared.isPlainObject(value)) {
for (const key in value) {
traverse(value[key], depth, seen);
}
}
return value;
}
watch除deep外,还有多个配置选项
1. immediate:监听器创建时,立即触发回调
2. flush:回调函数的触发时机,pre:dom更新前调用,post:dom更新后调用,sync:同步调用
3. onTrack/onTrigger:调试钩子,在依赖收集和回调函数触发时被调用
4. once:回调只在源变化时触发一次
watchEffect
当watch监听的对象较多时,书写逻辑比较复杂,可以利用watchEffect降低代码复杂度,同时,watchEffect会自动追踪响应式数据的变化
watchEffect(() => {
console.log(obj.a)
})
当我们希望在dom更新后再执行watch中的回调时,可以利用watchPostEffect函数
watchPostEffect(() => {
console.log('更新dom节点后的值:', obj.a)
})
同样,如果针对具体业务场景,可以直接对源码打业务补丁,对watch进行具体应用场景的watch封装