关于发布订阅模式
发布订阅模式应该算得上是前端框架中最常见的一种设计模式,vue的响应式原理,axios的取消发送等都使用到了发布订阅模式。
vue的响应式原理可以看我这篇文章[juejin.cn/post/716122…]
什么是发布订阅模式?
发布订阅模式是一种一对多的依赖关系,当一个状态发生改变时,所有依赖他的对象都得得到通知
结合现实举例
很多人去超市买东西,但是东西还没到货,老板说,你们先把自己的电话号码写本子上吧,到时候货到了我给你们打电话
很多人=>订阅者
老板=>发布者
把自己电话写本子上=>收集订阅者信息的过程
本子=>用于存放订阅者信息
打电话通知=>发布消息
发布订阅模式有哪些作用?
1.异步编程,在js中,我们无法获知一个异步任务什么时候完成,这时候可以在任务结束后发布消息,使外部订阅者获得通知,从而判断异步任务已完成
2.降低两个对象之间耦合度
发布订阅模式的应用
1.DOM事件
btn.addEventListener( 'click', function(){
alert(2);
}, false );
user.click(); // 模拟用户点击
在js中,事件的绑定就利用到了发布订阅模式,给按钮绑定一个事件,当事件被触发时,通知该函数
2.自定义模式
// 发布者Dep
class Dep {
constructor() {
this.dep = {}
}
//收集依赖
listen(key, call) {
if (!this.dep[key]) {
this.dep[key] = []
}
this.dep[key].push(call)
}
// 通知订阅者
trigger() {
const key = Array.prototype.shift.call(arguments)
const fns = this.dep[key]
if (!fns || fns.length == 0) {
return false
}
for (let i = 0; i < fns.length; i++) {
fns[i].apply(this, arguments)
}
}
// 取消订阅事件
remove(key, fn) {
const fns = this.dep[key]
if (!fns) {
return false
}
if (!fn) {
// 如果没有传入具体fn,则取消该key对应的所有fn
this.dep[key] = null
} else {
const index = fns.indexOf(fn)
this.dep[key].splice(index, 1)
}
}
}
必须先订阅再发布吗?
上面所说的所有情况都是先订阅再发布,那么可不可以先发布再订阅呢?答案是可以的,有的时候我们订阅者可能还没加载好,但是发布者就已经发布通知了。因此为了让订阅者不错过接收消息,我们可以创建一个存放离线事件的堆栈,当事件发布的时候我们将发布事件用函数包起来,存放到堆栈中,当订阅者准备好后,可以重新遍历堆栈,从而实现消息的重新发布。
发布订阅模式的优缺点
优点
缺点
消耗时间和内存:当订阅一个消息,但是该消息的发布者始终没有发布消息,该订阅者则会始终存放在内存中
参考: 《javascript设计模式与开发实践》