背景:在某一期需求(小程序)开发中,遇到了一小问题:两个不同的模块需要通信,完成一个事件交互。我想了半天:这个地方也没有像ng与vue里的事件派发机制,该怎么通知完成交互呢?最后还是没想出来,于是去请教了一起开发这个需求的同组大神:
我:大神,这个地方该怎么处理呢?
大神:这很简单,我们可以用观察者模式来解决。
我(一脸懵逼):啊,观察者模式是啥?(内心戏:啊,mmp,这是啥子哦,不懂撒)。
大神:嗯,没事,这块待会我来写,你可以先写其他的内容。
我:哦哦,好的。(内心戏:能解决就好,不过这个到底是个啥子东西哦)
复制代码
当时内心是崩溃的,借用电子竞技里很有名的一句话:菜是原罪。于是便有了今天的这篇分享,弥补下薄弱的基础0.0
一,什么是发布订阅模式
1,官方释义
发布---订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象
同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知。
复制代码
2,现实中的发布订阅
小红最近在淘宝网上看上一双鞋子,但是呢 联系到卖家后,才发现这双鞋卖光了,但是小红对这
双鞋又非常喜欢,所以呢联系卖家,问卖家什么时候有货,卖家告诉她,要等一个星期后才有货,
卖家告诉小红,要是你喜欢的话,你可以收藏我们的店铺,等有货的时候再通知你,所以小红收
藏了此店铺,但与此同时,小明,小花等也喜欢这双鞋,也收藏了该店铺;等来货的时候就依次会
通知他们;
复制代码
在上面的故事中,卖家是属于发布者,小红,小明等属于订阅者,订阅该店铺,卖家作为发布者,当鞋子到了的时候,会依次通知小明,小红等,依次使用旺旺等工具给他们发布消息。
二,实现发布订阅
1,简易的发布订阅
上面对发布订阅模式做了个简单的介绍,现在让我们来撸个简易版的发布订阅代码。
// 自定义一个事件管理对象
var evnetManager = {};
// 事件管理列表
eventManager.list = {};
// 监听事件
eventManager.subscribe = function (key, fn) {
// 判断该事件是否被监听
if (!this.list[key]) {
this.list[key] = [];
}
this.list[key].push(fn);
}
// 发布事件
eventManager.publish = function () {
// 利用arguments取参数
let key = [].shift.call(arguments), fns = this.list[key];
if (!fns || fns.length == 0) {
return false;
}
// 依次调用相关函数
fns.forEach((fn) => {
fn.apply(this, arguments);
})
}
复制代码
好了,上面是一个简易的发布订阅,具备监听与发布,现在让我们来验证下正确与否:
eventManager.subscribe('fire', function (person) {
console.log(person + '订阅了fire事件');
});
eventManager.publish('fire', '小红');
eventManager.publish('fire', '小明');
// 小红订阅了fire事件
// 小明订阅了fire事件
复制代码
nice,这样一个简易版的就实现了,但是不够通用化,让我们来写个标准版的。
2,用class类实现发布订阅
不多说,直接上菜:
// 声明事件管理类
class EventManager {
constructor () {
this.subscriberMap = {};
}
/**
* 订阅事件
* @param {*} hash
* @param {*} subscriber
*/
subscribe(hash, subscriber) {
var arr = this.subscriberMap[hash];
if (!arr) {
arr = this.subscriberMap[hash] = [];
}
// 判断事件对应的回调函数是否存在,防止重复添加。
if (!arr.includes(subscriber)) {
arr.push(subscriber);
}
}
/**
* 发布事件
* @returns {boolean}
*/
publish() {
let hash = [].shift.call(arguments);
let fns = this.subscriberMap[hash];
if (!fns || fns.length == 0) {
return false;
}
fns.forEach((fn) => {
fn.apply(this,arguments);
})
}
/**
* 移除某个事件的订阅
* @param hash {String}
* @param subscriber {ReceiveEventAgent}
*/
remove(hash, subscriber) {
var arr = this.subscriberMap[hash];
if (!arr) {
return false;
}
var index = arr.indexOf(subscriber);
if (index > -1) {
arr.splice(index, 1);
}
}
}
复制代码
好,现在我们已经声明了一个事件管理类,让我们来测试下:
function print(person) {
console.log(person + '订阅了事件');
}
var event = new EventManager();
event.subscribe('fire', print);
event.publish('fire', '小明'); // 小明订阅了事件
// 取消订阅(这里只是取消了print回调,并不是取消所有)
event.remove('fire', print);
复制代码
好,到此上面就是一个简易通用的发布订阅,可能功能还有待完善,但是应该可以满足一般的业务需求。
三,总结
优点:
- 时间上的解耦
- 对象之间的解耦
缺点:
- 消耗一定的时间和内存
- 使用过度,会导致程序难以跟踪维护和理解
这个时候让我们来仔细的回顾下,发现发布订阅模式的实现方式就是利用回调,每一个事件key数组,里面存储维护着多个不同的回调函数,当发布事件时,循环调用函数,从而实现发布订阅的效果,关键就是如何管理与维护。当然这些是我自己的一些理解,发布订阅实现的还不仅仅如此,学习的道路还很长,fighting !!!