概念
发布订阅是是一种设计模式,在这个设计模式中相当于有一个中介,负责事件的发布和订阅
我们把这个中介取名为 eventHub, 在 eventHub 中通常有 on(订阅), emit(发布), off(取消订阅) 等方法
const eventHub = {
on: (name, fn) => {},
emit: (name, data) => {},
off: (name, fn) => {}
}
我们还需要一个数据结构用来存储事件和对应的方法, 一种事件可以有多个订阅,每个订阅都对应不同的方法,且这些方法应该是要按订阅的顺序执行的,即这些方法是一个任务队列,我们可以把用数组来存储任务队列,
每个事件名都对应一个数组,就形成了映射,也称为哈希,因此我们声明一个eventMap
const eventMap = {}
on
先来写订阅方法 on
如果 eventMap[name] 不存在则初始化为空数组 []
并将 fn 插入到数组中
on: (name, fn) => {
eventMap[name] = eventMap[name] || []
eventMap[name].push(fn)
}
off
再写取消订阅方法
如果事件和方法存在,则删除
off: (name, fn) => {
if (!eventMap[name]) { return }
const index = eventMap[name].indexOf(fn)
if ( index >= 0) {
eventMap[name].splice(index, 1)
}
}
emit
发布方法 emit
emit: (name, data) => {
if (!eventMap[name]) { return }
eventMap[name].map(f => f.call(undefined, data))
}
使用
这样一个完整的eventHub就实现了
const eventMap = {}
const eventHub = {
on: (name, fn) => {
eventMap[name] = eventMap[name] || []
eventMap[name].push(fn)
},
emit: (name, data) => {
if (!eventMap[name]) { return }
eventMap[name].map(f => f.call(undefined, data))
},
off: (name, fn) => {
if (!eventMap[name]) { return }
const index = eventMap[name].indexOf(fn)
if ( index >= 0) {
eventMap[name].splice(index, 1)
}
}
}
我们来使用一下这个 eventHub
// 订阅
eventHub.on('click', console.log)
eventHub.on('click', console.error)
// 发布
eventHub.emit('click', 'hello')
once
我们还可以给eventHub添加一个 once 方法,once 的作用是只订阅一次,当触发事件后,自动取消订阅
once: (name, fn) => {
const _fn = () => {
fn()
eventHub.off(name, _fn)
}
eventHub.on(name, _fn)
}