这篇我将介绍vue3中的watch这个API,其实它也相当于一个effect。跟之前一样,我们先讲其用法,之后再介绍其源码。
watch用法
watch函数接受三个参数,第一个为监听的依赖值(有多种形态),第二个为依赖值变化后所要执行的回调,第三个为option,控制是否深度监听的deep,跟是否立即执行immediate。如下案例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app"></div>
<script src="./reactivity.global.js"></script>
<script>
const { reactive, effect, watch, ref} = VueReactivity
const personInfo = reactive({
name: '张三',
age: 18,
})
const name1 = ref('王五')
const age1 = ref(19)
// 1、传入函数
watch(() => personInfo.name, () => {
console.log('personInfo.name的值被改变了11')
})
// 2、传入响应式对象(这时默认时深度监听的,不推荐,比较耗性能)
watch(personInfo, () => {
console.log('personInfo.name的值被改变了22')
})
// 3、传入数组
watch([name1, age1], () => {
console.log('personInfo.name的值被改变了33')
})
// 重点:如果写成以下这样是不能做到监听的,因为这时他代表的是传入一个具体的值为张三,可想而知,把张三传进去肯定是不能做到响应式的
watch(personInfo.name, () => {
console.log('personInfo.name的值被改变了44')
})
setTimeout(() => {
personInfo.name = '李四'
name1.value = '小刚子'
}, 2000)
</script>
</body>
</html>
实现watch(核心代码)
import { ReactiveEffect } from "./effect";
import { isReactive } from "./reactive";
// 递归访问响应式对象的每个属性的目的是为了触发他的get方法,让其进行依赖收集
function traversal(value, set = new Set()) { // 考虑如果对象中有循环引用问题
// 递归终结条件:如果不是对象就不递归了
if(!isObject(value)) return value;
if(set.has(value)) return value;
set.add(value);
for(let key in value) {
traversal(value[key], set);
}
return value;
}
// source:用户传入的对象,cb:对应用户的回调
export function watch(source, cb) {
let getter;
if(isReactive(source)) { // 传入响应式对象
// 对我们用户传入的对象进行循环 (递归循环,只要循环就会访问对象上的每一个属性,访问属性的时候会收集effect)
getter = () => traversal(source);
}else if(isFunction(source)) { // 传入函数
getter = source;
}else { // 其它的方式暂未实现
return
}
let cleanup;
const onCleanup = (fn) => {
cleanup = fn; // 保存用户传入的函数
}
let oldValue;
const job = () => {
if(cleanup) cleanup(); // 下一次watch触发上一次watch的清理
const newValue = effect.run();
cb(newValue, oldValue, onCleanup);
oldValue = newValue; // 把新值赋值给oldValue,刷新老值
}
const effect = new ReactiveEffect(getter, job); // 监控自己构造的函数,变化后重新执行job方法
oldValue = effect.run();
}
总结
watch也是依赖effect来实现的,依赖值变化后执行对应函数,该函数想到与effect方法中的scheduler调度器。