1. 基本概念
发布-订阅模式是一种设计模式,用于实现对象之间的松散耦合。在这种模式下,发布者和订阅者之间并不直接通信,而是通过中介者来进行交流
- 发布者publisher:负责发布事件或消息给中介者
- 订阅者subscriber:订阅事件或消息,并在事件发生时被通知
- 中介者(主题或事件总线):负责管理订阅者和发布者之间的关系,并在事件发生时通知所有订阅者
特点:
- 松散耦合:通过中介者进行通信,系统更容易维护和扩展
- 异步通信:发布者可以随时发布事件,订阅者可以在准备好时接收和处理这些事件
- 多对多关系:一个事件可以有多个订阅者,一个订阅者可以订阅多个事件
2. 手写
class EventCenter {
constructor () {
// 事件中心
this.events = {}
}
/**
* 订阅事件
* @param {string} eventName - 事件名称
* @param {function} callback
* @memberof EventCenter
*/
subscribe (eventName, callback) {
// 确保当前 eventName 在事件中心是唯一的
if (!this.events[eventName]) {
// 创建事件容器
this.events[eventName] = []
}
// 存放事件
this.events[eventName].push(callback)
}
/**
* 取消订阅
* @param {string} eventName
* @param {function} callback
* @return {*}
* @memberof EventCenter
*/
unSubscribe(eventName, callback) {
// 事件中心里面没有这个事件
if (!this.events[eventName]) {
return new Error('not find event' + eventName)
}
// 只有事件名没有提供callback 删除对应事件名的所有订阅者(即事件数组)
if (!callback) {
delete this.events[eventName]
} else {
// 找到索引
const index = this.events[eventName].findIndex((el) => el === callback)
if (index !== -1) {
return new Error('not find callback')
}
// 移除事件下的某个函数
this.events[eventName].splice(index, 1)
// 查看事件容器是否为空 如果为空就移除事件
if (this.events[eventName].length === 0) {
delete this.events[eventName]
}
}
}
/**
* 触发事件
* @param {string} eventName
* @param {Array} args
* @return {*}
* @memberof EventCenter
*/
dispatch (eventName, ...args) {
if (!this.events[eventName]) {
return new Error('not find event' + eventName)
}
// 触发事件
this.events[eventName].forEach((el) => {
el(...args) // 调用每个订阅者的回调函数
})
}
}
// Test
const eventCenter = new EventCenter()
// 订阅事件
eventCenter.subscribe('click', (x, y) => {
console.log(`clicked at (${x}, ${y})`);
})
// 发布事件
eventCenter.dispatch('click', 10, 20) // output:clicked at (10, 20)