} else {
this.walk(value);
}
}
walk(obj) {
for (const [key, value] of Object.entries(obj)) {
defineReactive(obj, key, value);
}
}
}
再来验证一下:
const people = {
name: 'c',
age: 12,
parents: {
dad: 'a',
mom: 'b'
},
mates: ['d', 'e']};
new Observer(people);
people.mates[0]; // getter (2) ["d", "e"]people.mates.push('f'); // mutator: (2) ["d", "e"] ["f"]现在数组的修改也能被侦测到了。
依赖收集
目前已经可以对 Object 及 Array 数据的变化进行截获,那么开始考虑一开始提到的 Vue 响应式数据的第二个问题:如何知道数据变化后哪里需要修改?
最开始已经说过,Vue 中每个数据都需要收集与之相关的依赖,用来表示该数据变化时需要进行的操作行为。
通过数据的变化侦测我们可以知道数据何时被读取或修改,因此可以在数据读取时收集依赖,修改时通知依赖更新,这样就可以实现数据响应式了。
依赖收集在哪
为每个数据都创建一个收集依赖的对象 dep,对外暴露 depend(收集依赖)、notify(通知依赖更新)的两个方法,内部维护了一个数组用来保存该数据的每项依赖。
对于 Object,可以在 getter 中收集,setter 中通知更新,对 defineReactive 函数修改如下:
function defineReactive(data, key, val) {
let childOb = observe(val);
// 处理每个响应式数据时都创建一个对象用来收集依赖
let dep = new Dep();
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get: function () {
// 收集依赖
dep.depend();
return val;
},
set: function (newVal) {
if (val === newVal) {
return;
}
val = newVal;
// 通知依赖更新
dep.notify();
}
})
}
上面代码中依赖是收集在一个 Dep 实例对象上的,下面看一下 Dep 这个类。
class Dep {
constructor() {
this.subs = [];
}
addSub(sub) {
this.subs.push(sub);
}
removeSub(sub) {
if (this.subs.length) {
const index = this.subs.indexOf(sub);
this.subs.splice(index, 1);
}
}
depend() {
if (window.target) {
this.addSub(window.target);
}
}
notify() {
const subs = this.subs.slice();
for (let i = 0; i < subs.length; i++) {
subs[i].update();
}
}
}
Dep 的每个实例都有一个保存依赖的数组 subs,收集依赖时是从全局的一个变量上获取到并插入 subs,通知依赖时就遍历所有 subs 成员并调用其 update 方法。










