Dep
export default class Depend {
constructor() {
this.subs = [];
}
addSub(sub) {
console.log('addsub');
this.subs.push(sub);
}
notify() {
console.log('notify');
console.log(this.subs);
this.subs.forEach((e) => e.update());
}
}
Observer
export default class Observer {
constructor(value) {
this.walk(value);
}
walk(value) {
if (typeof value !== 'object' || !value) return;
Object.keys(value).forEach((e) => this.defineReactive(value, e, value[e]));
}
defineReactive(data, key, val) {
console.log(data, key, 'defineReactive');
this.walk(val);
const that = this;
const dep = new Depend();
Object.defineProperty(data, key, {
get() {
console.log('get', data, key);
Depend.target && dep.addSub(Depend.target);
return val;
},
set(newVal) {
if (newVal === val) return;
val = newVal;
that.walk(newVal);
dep.notify();
}
})
}
}
Watcher
export default class Watcher {
constructor(vm, key, cb) {
this.vm = vm;
this.key = key;
this.cb = cb;
Depend.target = this;
this.oldValue = this.getter(vm.$data);
Depend.target = null;
}
update() {
console.log('update');
const newVal = this.vm.$data[this.key];
if (newVal === this.oldValue) return;
this.cb(newVal);
}
getter(obj) {
const newVal = {};
const that = this;
Object.keys(obj).forEach((e) => {
if (typeof obj[e] === 'object' && obj[e] !== null) newVal[e] = that.getter(obj[e]);
else newVal[e] = obj[e];
});
return newVal;
}
}
Vue
export default class Vue {
constructor(options) {
this.$data = options.data || {};
new Observer(this.$data);
Object.keys(this.$data).forEach((k) => new Watcher(this, k, (newVal) => console.log('newVal --> ', newVal)));
}
}
测试
const obj = {
a: {
m: {
n: 5
},
b: 0
},
c: {
d: {
e: {
f: 6666
}
}
}
}
new Vue({
data: {
obj
}
});
setTimeout(() => {
obj.a.b = 1;
}, 2000);