Appearance
响应式系统
基本概念
响应式系统
- 响应式系统(Reactive System)是能够根据状态自动更新视图的系统。
- 响应式系统的作用是将状态与视图关联起来,当状态发生变化时,自动更新视图。
- 响应式系统的实现通常使用观察者模式,即当状态发生变化时,通知所有观察者进行更新。
观察者模式
- 观察者模式是一种设计模式,用于定义对象之间的一对多依赖关系,并实现当一个对象状态改变时,所有依赖于它的对象都会得到通知并自动更新。
- 观察者模式包含两个角色:观察者和被观察者。观察者订阅被观察者,当被观察者状态发生变化时,它会通知所有观察者进行更新。
状态
- 状态是指对象所处的某种特定的状态,它可以是任何类型的数据,如布尔值、整数、字符串等。
- 在响应式系统中,状态通常指的是数据模型中的数据,它可以是任何类型的数据,如模型对象、数据库中的数据等。
视图
- 视图是指用户界面的一部分,它可以是任何类型的组件,如按钮、输入框、列表等。
- 在响应式系统中,视图通常指的是与状态关联的组件,当状态发生变化时,它会自动更新以显示最新的数据。
依赖注入
- 依赖注入(Dependency Injection)是一种设计模式,用于解决对象之间的依赖关系。
- 在响应式系统中,依赖注入通常用于将状态注入到视图中,以便在状态发生变化时,视图能够自动更新。
实现
Vue 2 使用 getter / setters 完全是出于支持旧版本浏览器的限制。而在 Vue 3 中则使用了 Proxy 来创建响应式对象。
js
// `Dep` 类是一个依赖管理器,它内部维护了一个订阅者集合(subscribes)。
// `addSubscribe` 方法用于添加订阅者,`notify` 方法用于通知所有订阅者更新
class Dep {
constructor() {
this.subscribes = new Set();
}
addSubscribe(watcher) {
this.subscribes.add(watcher);
}
notify() {
this.subscribes.forEach(watcher => watcher());
}
}
// `activeUpdate` 是一个全局变量,用于记录当前正在执行的更新函数。
// `autorun` 函数接受一个更新函数,当数据发生变化时,这个更新函数会被调用。
let activeUpdate = null;
function autorun(update) {
function wrappedUpdate() {
activeUpdate = wrappedUpdate;
update();
activeUpdate = null;
}
wrappedUpdate();
}
// `observable` 函数接受一个对象,然后遍历这个对象的所有属性,
// 使用`Object.defineProperty`方法劫持每个属性的getter和setter。
// 在getter中收集依赖,在setter中触发更新。
function observable(data) {
Object.keys(data).forEach(key => {
const dep = new Dep();
let value = data[key];
Object.defineProperty(data, key, {
get() {
dep.addSubscribe(activeUpdate);
return value;
},
set(newValue) {
if (newValue === value) return;
value = newValue;
dep.notify();
}
})
})
return data
}
// 使用示例
const state = observable({
name: 'Alice'
});
autorun(() => {
console.log(state)
console.log(`Name is: ${state.name}`);
});
setTimeout(() => {
state.name = 'Bob';
}, 5000);解释
以上实例中,我们创建了一个可观察的对象state,并且通过autorun函数模拟了一个绑定到UI的操作。当我们修改state.name时,这个变化会通过setter触发,然后Dep实例会通知所有依赖更新,即重新执行autorun中传入的函数,从而实现了数据到UI的同步。 要实现UI到数据的同步,通常需要监听UI上的事件(例如输入框的input事件),然后在事件处理函数中更新数据模型。这部分通常由框架自动完成。
思考
如何使用proxy实现双向绑定?