title: Vue双向绑定中的发布-订阅模式究竟是什么 date: 2021-06-07 22:47:00 updated: 2021-06-07 22:47:00 photos:
发布订阅模式不管是在程序还是现实生活中都非常地常见,例如,过年或者节假日火车票买不到票时,购票软件会有一个候补下单的功能,用户可以向购票软件后台发出一个订阅请求:如果有人退票或某些原因增加座位后,软件后台会帮助用户下单并及时通知用户成功购买该车次车票。
这就是一个发布-订阅的例子,购票软件是一个发布消息的发布者
,而用户则是订阅消息的订阅者
。可以看到,这是一个一对多
的依赖关系,当发布的对象状态发生改变时,所有依赖他的对象都将得到通知。[1]
这就是发布-订阅模式的定义。
了解了发布订阅模式的定义,再来看一下 vue 双向绑定的原理。
下面是 vue 官网双向绑定原理的图,他是通过Object.defineProperty
或者new Proxy
去监听对象数据变化,对象相当于一个发布者(publisher),当监听到对象改变后对象的setter
就会发送消息通知订阅者(watcher),订阅者收到消息后通知对 DOM 文本进行update
操作。
上面图是用 Proxy 实现一个双向绑定的部分代码,从代码片段中可以看到observer
作为一个发布者,它可以同时被多个变量订阅,当监听到数据发生改变时,调用set
方法通知watcher(订阅者)
去改变 node 节点和值。
同理,上图代码描述的是,当我们给input
输入框作为一个发布者,watcher
作为订阅者。当用户输入数据时,发布者(input) 向订阅者(watcher) 发送改变信息的通知,watcher
收到通知后进行值得修改和 node 节点数据更改,这就是 vue 通过发布订阅模式实现的数据双向绑定。
除了 vue 中数据双向绑定使用的是发布订阅模式,相信我们在 JavaScript 也会经常使用到的事件
也是一个发布订阅模式。例如addEventListener
:
document.body.addEventListener("click", () => {
console.log("click 111");
});
document.body.addEventListener("click", () => {
console.log("click 222");
});
document.body.addEventListener("click", () => {
console.log("click 333");
});
在给 body 订阅了点击事件后,点击 body 都会
依次
触发上面的三个函数。
又例如,在 node 中的EventEmitter
类
const event = require("events").EventEmitter;
event.on("data", () => {
console.log("111");
});
event.on("data", () => {
console.log("222");
});
event.on("data", () => {
console.log("333");
});
setTimeout(() => {
event.emit("data");
}, 1000);
可见,发布-订阅模式在 JavaScript 中非常有用而且使用广泛。
了解了发布订阅的模式后,最后我们来实现一个通用的发布订阅模式函数吧~
class CommonEvent {
constructor() {
this.events = {};
}
// 订阅函数
listen(name, eventFun) {
if (!this.events[name]) this.events[name] = [];
if (typeof eventFun !== "function") throw "eventFun must be a function!";
this.events[name].push(eventFun);
}
// 触发事件函数
emit(name, ...params) {
if (!this.events[name]) return;
this.events[name].forEach((val) => {
val(...params);
});
}
// 清除订阅事件
remove(name, eventFun) {
if (!this.events[name]) return;
this.events[name] = this.events[name].filter((val) => val !== eventFun);
console.log(this.events[name]);
}
}
const event = new CommonEvent();
function handleFun(msg) {
console.log(msg);
}
event.listen("data", handleFun);
event.listen("data", handleFun);
event.listen("data", handleFun);
event.emit("data", "111"); // 111 111 111
event.remove("data", handleFun); // []
使用发布订阅模式编写程序,他的优点有:
1. 实现时间上的解耦(组件,模块之间的异步通讯)
2. 对象之间的解耦,交由发布订阅的对象管理对象之间的耦合关系
当然发布订阅模式也存在缺点:
1. 创建的订阅者本身要消耗一定的时间和内存,当我们订阅一个消息后,如果不去销毁,这个订阅这会一直存在于内存之中。
2. 对象之间解耦的同时,他们的关系也会被深埋在代码背后,这会造成一定的维护成本
本文主要介绍了设计模式中的发布订阅模式
阅读文本可以了解到: