什么是响应式?
就是你操作对象,对象就会给你一定的反应,这就是响应式。所以数据响应式就是你操作数据,数据就做出相应的反应。
比如:修改了vue 中data 的数据,视图中的data数据就会被直接修改。
那么vue是如何实现数据响应式的呢?
实现数据响应式中的三个主要角色
- 紫色的
Observer和Dep:Observer是给对象的属性添加geteter和setter,用于收集依赖和派发更新。Dep是收集当前对象的依赖关系,当对象的数据发生变化,dep.notify通知watcher改变数据
- 蓝色的
Watcher:观察者对象,实例有渲染watcher(render water),计算属性 watcher (computed watcher),侦听器 watcher(user watcher三种
当然,文字的描述是很难理解清楚它们的关系和作用,可以看看下图。reactive可以理解为图1中的紫色部分
可以看见,一个属性对应了一个Dep和可能多个Component(因为这个属性可能会被多个地方使用),每一个Component对应一个Watch,Dep是属性的和Watch的中间者,换个专业的是说法Dep帮助我们依赖管理
大致思路:Observer和Dep对传入的数据进行监听和代理,当数据发送变化时,调用dep.notify来通知Watcher,Watcher接受到通知,根据通知去改变数据,并渲染到视图中。
核心代码
/**
* @name Vue数据双向绑定(响应式系统)的实现原理
*/
// observe方法遍历并包装对象属性
function observe(target) {
// 若target是一个对象,则遍历它
if (target && typeof target === "Object") {
Object.keys(target).forEach((key) => {
// defineReactive方法会给目标属性装上“监听器”
defineReactive(target, key, target[key]);
});
}
}
// 定义defineReactive方法
function defineReactive(target, key, val) {
const dep = new Dep();
// 属性值也可能是object类型,这种情况下需要调用observe进行递归遍历
observe(val);
// 为当前属性安装监听器
Object.defineProperty(target, key, {
// 可枚举
enumerable: true,
// 不可配置
configurable: false,
get: function () {
return val;
},
// 监听器函数
set: function (value) {
dep.notify();
},
});
}
class Dep {
constructor() {
this.subs = [];
}
addSub(sub) {
this.subs.push(sub);
}
notify() {
this.subs.forEach((sub) => {
sub.update();
});
}
}