大家做VUE开发的过程中,都会在官网看到一句话:vue会遍历data,通过 Object.defineProperty方法对数据进行劫持,使其转换为响应式。
看了源码之后,我想自己模仿着写一个简单的例子,去模拟下这种订阅发布模式。以下为代码实现
// 发布者
class Dep{
constructor() {
this.subs = [];
}
addSubs(sub) {
if (this.subs.indexOf(sub) < 0) {
this.subs.push(sub);
}
}
notify() {
this.subs.forEach(item => item.update())
}
}
Dep.target = null;
// 订阅者
class Watcher {
constructor(obj, key, updateCb) {
this.data = obj;
this.key = key;
this.updateCb = updateCb;
this.value = null;
this.get();
}
get() {
Dep.target = this;
this.value = this.data[this.key];
Dep.target = null;
return this.value;
}
update() {
const oldValue = this.value;
const newValue = this.get();
this.updateCb(newValue, oldValue);
}
}
// observer类 劫持数据
class Observer {
constructor(obj) {
this.data = obj;
if (this.data == null || typeof this.data !== "object") {
return;
}
if (Array.isArray(this.data)) {
this.observeArray();
} else {
this.walk();
}
}
walk() {
for (let i in this.data) {
this.defineReactive(this.data, i);
}
}
observeArray() {
for (let i = 0; i < this.data.length; i++) {
observe(this.data[i]);
}
}
defineReactive(obj, key) {
let val = obj[key];
observe(val);
const dep = new Dep();
Object.defineProperty(obj, key, {
get() {
if (Dep.target) {
dep.addSubs(Dep.target)
}
return val;
},
set(newVal) {
if (newVal === val) {
return;
}
val = newVal;
observe(val);
dep.notify();
}
})
}
}
// 数据监测方法
function observe(data) {
new Observer(data);
}
// 写一个最简单的例子
const obj = {
a: 1
};
observe(obj);
new Watcher(obj, "a", (newVal, oldVal) => {
console.log("newVal", newVal);
console.log("oldVal", oldVal + '\n');
});
obj.a = 2;
obj.a = 3;
最终控制台输出的结果在我们的意料之中
一个简单的数据劫持功能就做好了。
当然这里考虑的场景是极为简单的,只支持做简单的效果。比如我写的Watcher类就不能够监听深层级的属性或者数组中某个元素属性的变化。但是用来理解订阅发布模式的思想,应该是比较简单的。