核心概念
在 Vue.js 中,底层的依赖追踪是通过一个名为「依赖收集器」(Dependency Collector)的机制实现的。依赖收集器负责追踪哪些响应式数据(如响应式对象或计算属性)在当前执行的渲染函数中被访问了,并记录它们的依赖关系。
- 具体实现包括以下几个核心概念:
- Dep(依赖) :
Dep是一个类,表示一个可观察的依赖项。每个响应式对象的属性都会有一个关联的Dep对象。在依赖收集的过程中,它会记录所有观察者对象。 - Watcher(观察者) :
Watcher是一个类,用于监听依赖项的变化。当依赖项发生改变时,Watcher的回调函数就会被调用,从而触发更新。 - Track(追踪) :
Track函数用来追踪依赖收集过程。当访问一个响应式对象的属性时,Track函数会被调用。在Track函数中,会通过Dep 类的静态方法target来获取当前正在执行的Watcher` 实例,并将当前的依赖项添加到它的依赖列表中。 - Trigger(触发) :
Trigger函数用来触发更新。当依赖项发生变化时,会调用与之关联的Dep的notify方法,遍历该Dep对象的所有观察者,依次调用它们的回调函数。
实现一个简化版的响应式数据系统
ref 的实现
ref函数的实现。它接受一个初始值initialValue,并在函数内部定义了一个局部变量value来保存数据的值。ref函数还创建了一个Dep的实例对象dep,用于管理依赖关系。然后,通过返回一个对象字面量的方式,暴露了两个属性value和set value(newValue),分别用于获取和设置数据的值。 在get value的属性访问器中,首先调用dep.depend()将当前正在执行的Watcher添加到依赖列表中,然后返回value的值。在set value(newValue)的属性设置器中,首先判断传入的新值newValue是否与旧值value相等。如果不相等,就将value更新为newValue,然后调用dep.notify()触发依赖项的更新。
function ref(initialValue) {
let value = initialValue;
const dep = new Dep();
return {
get value() {
// 当访问 ref.value 时,将当前正在执行的 Watcher 添加到依赖中
dep.depend();
return value;
},
set value(newValue) {
if (newValue !== value) {
value = newValue;
// 触发依赖项的更新
dep.notify();
}
},
};
}
Dep 构造函数的实现
Dep类负责管理依赖关系,在其构造函数中初始化了一个subscribers数组,用于保存依赖关系(Watcher),Dep类提供了depend方法用于将当前正在执行的Watcher添加到依赖列表中,以及notify方法用于触发依赖项的更新。
let activeWatcher = null
class Dep {
constructor() {
this.subscribers = [];
}
depend() {
if (activeWatcher) {
this.subscribers.push(activeWatcher);
}
}
notify() {
this.subscribers.forEach(subscriber => subscriber.update());
}
}
watch 函数实现
它接受两个参数
target和callback,其中target是一个函数,用于计算所监听的数据,callback是数据变化时的回调函数。在watch函数内部,首先创建一个Watcher实例对象watcher,通过传入的target和callback参数进行初始化。然后调用watcher.get(),这一步的目的是为了触发一次数据获取,以收集依赖关系。
function watch(target, callback) {
const watcher = new Watcher(target, callback);
// 初始触发一次,用于收集依赖
watcher.get();
}
Watcher 构造函数实现
Watcher类是实际上的数据监听者,它接受一个target和callback参数并保存起来。在构造函数中,同时调用了this.get()方法,这一步的目的是为了初始化value属性,并触发一次数据获取以收集依赖关系。get方法的作用是获取数据,并将当前Watcher实例对象设置为活跃的activeWatcher,然后调用target函数来获取数据的最新值,并将该值保存到value属性中,最后再将activeWatcher重置为null。update方法用于更新数据,它首先调用this.get()来获取最新的值,并与旧值this.value进行比较。如果新值和旧值不相等,就将value更新为新值,并调用回调函数callback进行通知。
class Watcher {
constructor(target, callback) {
this.target = target;
this.callback = callback;
this.value = this.get();
}
get() {
// 设置当前活跃的 Watcher 为 this
activeWatcher = this;
// 触发ref里面的get
const value = this.target();
// 当depend方法执行完成后、重置当前活跃的 Watcher
activeWatcher = null;
return value;
}
update() {
const newValue = this.get();
if (newValue !== this.value) {
this.value = newValue;
this.callback(newValue);
}
}
}
最后简单的响应式就完成了
这段代码实现了一个简单的响应式数据系统,通过
ref函数和watch函数提供了对数据的监听和响应机制。当依赖的数据发生变化时,会触发相应的回调函数进行处理。
function ref(initialValue) {
let value = initialValue;
const dep = new Dep();
return {
get value() {
// 当访问 ref.value 时,将当前正在执行的 Watcher 添加到依赖中
dep.depend();
return value;
},
set value(newValue) {
if (newValue !== value) {
value = newValue;
// 触发依赖项的更新
dep.notify();
}
},
};
}
function watch(target, callback) {
const watcher = new Watcher(target, callback);
// 初始触发一次,用于收集依赖
watcher.get();
}
let activeWatcher = null;
class Dep {
constructor() {
this.subscribers = [];
}
depend() {
if (activeWatcher) {
this.subscribers.push(activeWatcher);
}
}
notify() {
this.subscribers.forEach(subscriber => subscriber.update());
}
}
class Watcher {
constructor(target, callback) {
this.target = target;
this.callback = callback;
this.value = this.get();
}
get() {
// 设置当前活跃的 Watcher 为 this
activeWatcher = this;
// 触发ref里面的get
const value = this.target();
// 当depend方法执行完成后、重置当前活跃的 Watcher
activeWatcher = null;
return value;
}
update() {
const newValue = this.get();
if (newValue !== this.value) {
this.value = newValue;
this.callback(newValue);
}
}
}
const count = ref(0);
console.log(count);
watch(() => count.value, (newValue) => {
console.log(`count 的值已改变为:${newValue}`);
});
setTimeout(() => {
count.value = 1; // 输出:count 的值已改变为:1
}, 5000)