突然想到了两种模式。观察者模式和消息订阅与发布模式。本文会简单的梳理一下Vue当中所用到的观察者模式所实现的数据劫持。
我是按照我的理解,对数据劫持进行了倒推,方便理解。
比如:我要对一个对象进行劫持,检测这个对象某个属性发生了变化,那我需要先有个对象,然后再对这个对象进行劫持。
const data = { name: 'Vue' };
observe(data);
这里的observe方法就是一个观察方法。先看一下代码
// 递归地将对象的所有属性都转换为响应式的
function observe(value) {
if (!isObject(value)) {
return;
}
if (typeof value === 'object') {
Object.keys(value).forEach(key => {
defineReactive(value, key, value[key]);
});
}
}
这个方法接收对象之后进行了一个判断(是否为对象类型),核心劫持的逻辑就在defineReactive这个代码。
function defineReactive(obj, key, val) {
// 递归地将所有属性都转换为响应式的
observe(val);
// 创建一个内部依赖列表
const dep = new Dep();
// 使用Object.defineProperty来定义属性的getter和setter
Object.defineProperty(obj, key, {
enumerable: true, // 可枚举
configurable: true, // 可配置
get: function reactiveGetter() {
// 当属性被访问时,通知依赖
Dep.target && dep.depend();
return val;
},
set: function reactiveSetter(newVal) {
// 检查新值是否与旧值相同
if (newVal === val) return;
// 更新值
val = newVal;
// 递归地将新值转换为响应式的
observe(newVal);
// 通知所有依赖该属性的watcher更新
dep.notify();
}
});
}
这里面有一个依赖(Dep)
// 依赖收集类
class Dep {
constructor() {
this.subscribers = new Set();
}
depend() {
if (Dep.target) {
this.subscribers.add(Dep.target);
}
}
notify() {
this.subscribers.forEach(subscriber => {
subscriber.update();
});
}
}
// 存放当前正在计算的watcher
Dep.target = null;
最后就是观察数据了
// 简单的watcher类
class Watcher {
constructor(obj, key, cb) {
Dep.target = this; // 将当前watcher设置为Dep.target
this.cb = cb;
this.obj = obj;
this.key = key;
// 触发getter,导致依赖收集
this.value = obj[key];
Dep.target = null; // 清理Dep.target
}
update() {
this.cb(this.obj[this.key]);
}
}
通过触发这个类,来监听我们想要监听的数据
const watcher = new Watcher(data, 'name', function(newVal) {
console.log(`name has changed to ${newVal}`);
});
修改data的name属性就可以出发上面的打印
下面为完整代码
function defineReactive(obj, key, val) {
// 递归地将所有属性都转换为响应式的
observe(val);
// 创建一个内部依赖列表
const dep = new Dep();
// 使用Object.defineProperty来定义属性的getter和setter
Object.defineProperty(obj, key, {
enumerable: true, // 可枚举
configurable: true, // 可配置
get: function reactiveGetter() {
// 当属性被访问时,通知依赖
Dep.target && dep.depend();
return val;
},
set: function reactiveSetter(newVal) {
// 检查新值是否与旧值相同
if (newVal === val) return;
// 更新值
val = newVal;
// 递归地将新值转换为响应式的
observe(newVal);
// 通知所有依赖该属性的watcher更新
dep.notify();
}
});
}
// 依赖收集类
class Dep {
constructor() {
this.subscribers = new Set();
}
depend() {
if (Dep.target) {
this.subscribers.add(Dep.target);
}
}
notify() {
this.subscribers.forEach(subscriber => {
subscriber.update();
});
}
}
// 存放当前正在计算的watcher
Dep.target = null;
// 简单的watcher类
class Watcher {
constructor(obj, key, cb) {
Dep.target = this; // 将当前watcher设置为Dep.target
this.cb = cb;
this.obj = obj;
this.key = key;
// 触发getter,导致依赖收集
this.value = obj[key];
Dep.target = null; // 清理Dep.target
}
update() {
this.cb(this.obj[this.key]);
}
}
// 递归地将对象的所有属性都转换为响应式的
function observe(value) {
if (!isObject(value)) {
return;
}
if (typeof value === 'object') {
Object.keys(value).forEach(key => {
defineReactive(value, key, value[key]);
});
}
}
function isObject(value) {
return value !== null && typeof value === 'object';
}
// 示例用法
const data = { name: 'Vue' };
observe(data);
const watcher = new Watcher(data, 'name', function(newVal) {
console.log(`name has changed to ${newVal}`);
});
// 修改name属性以触发watcher
data.name = 'Reactivity';