订阅发布者模式 subacribing/publisher 是一种开发模式。
优点:
1.时间上的解耦
2.对象之间的解耦
缺点: 发布订阅模式本身就消耗一定的时间和内存
这种模式由订阅者、处理中心、发布者三部分组成。订阅者只管在处理中心订阅,有人订阅它才响应。发布者触发时只通知处理中心,并不关心响应什么。
接下来实现一个订阅发布者的类
class Event {
constructor() { }
// 做一个事件集合
handlers = {}
// 事件添加器 也就是订阅者
//接收两个参数 一个事件名称 一个事件方法
//一个名称下可以有多个方法
addEventListener(type, handler) {
//判断type是否已在handlers中存在
if (!(type in handlers)) {
//不存在 建立数组存储
this.handlers[type] = []
}
this.handlers[type].push(handler)
}
//发布者 也就是响应订阅的方法
// 两个参数 一个需要触发的事件名 一个响应方法的参数
dispatchEvent(type, ...params) {
if (!(type in handler)) {
return new Error('未注册该事件')
}
this.handlers[type].forEach(ele => {
ele(...params)
});
}
// 取消订阅 也就是取消已经订阅的响应方法
//两个参数 一个事件名 一个需要取消的方法
removeEventListener(type, handler) {
//先判断事件是否存在
if (!(type in this.handlers)) {
return new Error('无效事件')
}
// 先判断第二个参数传递了没 要是没有 就删除事件
if (!handler) {
delete this.handlers[type]
} else {
// 有的话就查找是哪个 找到索引然后删除
const ids = this.handlers[type].findIndex(item => item === handler)
//这一步就是校验为handler空的情况
if (ids === undefined) {
return new Error('无该绑定事件')
}
this.handlers[type].splice(ids,1)
//删除完后查看是否为空
if( this.handlers[type].length==0){
delete this.handlers[type]
}
}
}
}
这就简单完成了订阅者、 发布者、 处理中心。
使用情况
let event = new Event()
function load (params) {
console.log('load', params);
}
function load2 (params) {
console.log('load2', params);
}
event.addEventListener('load', load)
event.addEventListener('load', load2)
event.dispatchEvent('load', 'load事件触发')
// 删除全部load事件
event.removeEventListener('load')
介绍一个简单的使用场景 在a模块中点击按钮 在b模块中显示实时增加的count变量
b模块订阅一个事件 给id为b的区域写入值为num a模块发布一个事件,每次点击按钮都激发 , 传入参数 ,b模块就能实施更新
//a模块
let count = 0
//给按钮添加点击事件
button.onclick = function () {
event.dispatchEvent(addCount, ++count)
}
//b模块
function add (num) {
document.getElementById('b').innerHTML = num
}
event.addEventListener('addCount', add)
扩展
目前的模式都是先订阅后发布 假如先发布了就没有响应了。在使用场景中很可能就会用到先发布后订阅的情况,比如qq消息在离线后进行一个缓存聊天记录,登录的时候再把聊天记录从新渲染一遍,这时候就会用到,先发布,登录的时候再订阅的情况。我们可以对上面的Event类进行一个改动,增加缓存区。
主要改的地方是 订阅 和 发布的判断
class Event {
constructor() { }
handlers = {}
//增加缓存区
cache = {}
addEventListener (type, handler) {
//订阅的时候发现缓存区有这个事件
//就触发 并且是缓存只触发一次 触发完就删掉该缓存
if (type in this.cache) {
//如果存在就触发 并把缓存的参数传入
handler(this.cache[type])
delete this.cache[type]
}
if (!(type in this.handlers)) {
this.handlers[type] = []
}
this.handlers[type].push(handler)
}
dispatchEvent (type, ...params) {
// 先发布 假如发布的时候没有type事件
// 那就把参数先存起来
if (!(type in this.handlers)) {
this.cache[type] = params
return new Error('未注册该事件,存入缓存')
}
this.handlers[type].forEach(ele => {
ele(...params)
});
}
removeEventListener (type, handler) {
if (!(type in this.handlers)) {
return new Error('无效事件')
}
if (!handler) {
delete this.handlers[type]
} else {
const ids = this.handlers[type].findIndex(item => item === handler)
if (ids === undefined) {
return new Error('无该绑定事件')
}
this.handlers[type].splice(ids, 1)
if (this.handlers[type].length == 0) {
delete this.handlers[type]
}
}
}
}