Vue3的watch底层源码主要是通过使用Proxy对象来实现的。在Vue3中,每个组件实例都会有一个watcher实例,用于监听组件数据的变化。当组件数据发生变化时,watcher实例会触发回调函数,从而更新组件的视图。
Vue3的watch底层源码主要涉及到以下几个部分:
- 创建watcher对象
- 收集依赖
- 触发更新
下面对这三个部分进行详细解读。
- 创建watcher对象
在Vue3中,watcher对象是通过watchEffect
函数创建的。watchEffect
函数接收一个函数作为参数,这个函数就是我们要监听的响应式数据的getter函数。watchEffect
函数会在创建watcher对象时立即执行这个函数,并收集响应式数据的依赖。
创建watcher对象的源码如下:
function watchEffect(effect, options) {
const watcher = new ReactiveEffect(effect, options)
watcher.run()
return () => {
watcher.stop()
}
}
其中,ReactiveEffect
是一个类,用来创建watcher对象。run
方法会执行传入的函数,并收集依赖。stop
方法会停止watcher对象的执行。
- 收集依赖
在Vue3中,依赖收集是通过track
函数实现的。track
函数接收两个参数:响应式数据对象和属性名。它会在getter函数执行时,将当前的watcher对象收集到响应式数据对象的依赖列表中。
track
函数的源码如下:
function track(target, key) {
if (activeEffect === undefined) {
return
}
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = new Set()))
}
if (!dep.has(activeEffect)) {
dep.add(activeEffect)
activeEffect.deps.push(dep)
}
}
其中,targetMap
是一个全局的Map对象,用来存储所有响应式数据对象的依赖关系。activeEffect
是一个全局的watcher对象,表示当前正在执行的watcher对象。
- 触发更新
在Vue3中,更新是通过trigger
函数实现的。trigger
函数接收两个参数:响应式数据对象和属性名。它会在setter函数执行时,遍历响应式数据对象的依赖列表,依次执行每个watcher对象的run
方法,从而触发更新。
trigger
函数的源码如下:
function trigger(target, key) {
const depsMap = targetMap.get(target)
if (!depsMap) {
return
}
const dep = depsMap.get(key)
if (!dep) {
return
}
dep.forEach(effect => {
if (effect.scheduler) {
effect.scheduler()
} else {
effect.run()
}
})
}
其中,scheduler
是一个可选的参数,表示watcher对象的调度函数。如果存在调度函数,则会先执行调度函数,再执行run
方法。
下面是watch底层源码的主要实现步骤:
-
创建一个Proxy对象,用于监听数据的变化。
-
在Proxy对象的set方法中,触发watcher实例的回调函数。
-
在watcher实例的回调函数中,执行用户定义的回调函数,并将新旧值传递给回调函数。
-
在回调函数中,执行用户定义的逻辑,例如更新组件的视图。
下面是watch底层源码的简单实现:
function watch(source, callback) {
const watcher = {
callback: callback,
oldValue: null,
update() {
const newValue = source();
if (newValue !== this.oldValue) {
this.callback(newValue, this.oldValue);
this.oldValue = newValue;
}
}
};
watcher.update();
return watcher;
}
function reactive(obj) {
return new Proxy(obj, {
set(target, key, value) {
target[key] = value;
watchers.forEach(watcher => watcher.update());
return true;
}
});
}
const data = reactive({ count: 0 });
const watchers = [];
watchers.push(watch(() => data.count, (newValue, oldValue) => {
console.log(`count changed from ${oldValue} to ${newValue}`);
}));
data.count++; // count changed from 0 to 1
在上面的代码中,我们定义了一个watch函数和一个reactive函数。watch函数用于创建watcher实例,reactive函数用于创建Proxy对象。当数据发生变化时,Proxy对象的set方法会触发watcher实例的回调函数,从而更新组件的视图。