<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>
<div id="app">
<p>{{ name }} is {{ age }} years old.</p>
<button @click="updateData">Update Data</button>
</div>
<script>
new Vue({
el: '#app',
data: {
name: 'Alice',
age: 30
},
methods: {
updateData() {
this.name = 'Bob';
this.age = 35;
}
}
});
}
// 假设这里是 Vue 的内部实现部分
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
// 依赖收集
if (Dep.target) {
dep.depend();
}
return val;
},
set(newVal) {
if (val === newVal) return;
val = newVal;
// 触发依赖更新
dep.notify();
}
});
}
function observe(obj) {
if (!obj || typeof obj!== 'object') return;
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key]);
});
}
class Dep {
constructor() {
this.subs = [];
}
addSub(sub) {
this.subs.push(sub);
}
depend() {
if (Dep.target) {
this.addSub(Dep.target);
}
}
notify() {
this.subs.forEach(sub => {
sub.update();
});
}
}
Dep.target = null;
function watcher(fn) {
this.fn = fn;
this.update = () => {
this.fn();
};
Dep.target = this;
this.fn();
Dep.target = null;
}
observe(new Vue().$data);
new watcher(() => {
console.log('依赖收集触发了此函数,数据变化会再次触发');
document.getElementById('app').querySelector('p').textContent = `${new Vue().$data.name} is ${new Vue().$data.age} years old.`;
});
</script>
</body>
</html>
在上述代码中,我们模拟了 Vue 2 的依赖收集过程:
首先,在 defineReactive 函数中,当属性被读取时(即触发 get 方法),检查 Dep.target 是否存在。如果存在,说明有地方在读取这个属性,需要进行依赖收集。
在 depend 方法中,将当前的依赖(这里是一个 watcher 实例)添加到 Dep 的订阅列表中。
在 watcher 的构造函数中,将当前 watcher 实例设置为 Dep.target,然后执行传入的函数,这个函数通常是渲染函数或者计算属性的函数。在执行这个函数的过程中,如果读取了响应式数据的属性,就会触发 get 方法,从而进行依赖收集。执行完函数后,将 Dep.target 重置为 null。
当页面加载时,会执行 observe 函数对 data 对象进行响应式处理,并创建一个 watcher 实例,这个实例的构造函数会触发依赖收集过程。当模板中读取 name 和 age 的值时,就会将这个 watcher 实例添加到相应属性的依赖列表中。这样,当 name 或 age 的值发生变化时,就可以通知这个 watcher 实例进行更新。