如何写一个 简单的 发布订阅模式 demo
发布订阅模式 是一个很常见的 异步编程模式。 主要是通过把 回调函数 事件化来实现该模式
- 订阅者
- 发布者
- 信号中心
关于这 3 者的意思呢,网上众说纷纭。我感觉怎么理解都是对的。
那就写一个可以 可以收集订阅者注册的事件,并且可以通知订阅者的 dome
开始
- 这里我们用
class表示。并初始化一个subs用于存放 事件 的对象
class EventEmitter {
constructor() {
this.subs = Object.create(null)
}
}
- 把
subs设计成一个对象,把 键 作为事件名称,值 为一个数组用于存放事件动作。如下所示
type Subs = { [key: string]: [Function] }
$on 订阅事件/注册事件
- 如何使用
第一个参数是 事件名称 ,第二个参数是 处理函数
function handleClick() {
console.log('clicked')
}
em.$on('click', handleClick)
- 如何实现
把事件名称 作为 key,赋值为一个数组。并把处理函数放入数组中
$on (eventType, handler) {
this.subs[eventType] = this.subs[eventType] || []
this.subs[eventType].push(handler)
}
$emit 发布通知/触发事件
- 如何使用
很简单只需要传入要触发的事件名称即可
em.$emit('click')
- 如何实现
用 事件名称 获取到 subs 中对应的 事件动作 集合。并循环执行即可
$emit (eventType) {
this.subs[eventType].forEach(handler => handler())
}
完整代码
class EventEmitter {
constructor() {
this.subs = Object.create(null)
}
$on (eventType, handler) {
this.subs[eventType] = this.subs[eventType] || []
this.subs[eventType].push(handler)
}
$emit (eventType) {
this.subs[eventType].forEach(handler => handler())
}
}
// Test
const em = new EventEmitter()
function handleClick () {
console.log('clicked')
}
em.$on('click', handleClick)
em.$on('click', () => {
console.log('clicked2')
})
em.$on('change', () => {
console.log('changed')
})
em.$emit('click') // > clicked clicked2
em.$emit('change') // > changed
触发事件时,传入参数
当我们想在 发布通知/触发事件 告诉订阅者,点击的是谁、改变后的值是什么。这时候我们就需要在 触发 时可以传入参数
- 如何使用
em.$emit('eventName', 'params1', 'params1') // > clicked clicked2
- 如何实现
修改下 $emit 代码
$emit (eventType, ...params) {
this.subs[eventType].forEach(handler => handler(...params))
}
- 注册时
function handleClick (params1) {
console.log('clicked', params1)
}
em.$on('click', handleClick)
em.$on('click', (params1) => {
console.log('clicked2', params1)
})
em.$on('change', (params1, params2) => {
console.log(`changed:通过${params1},改变成${params2}`)
})
- 触发时
em.$emit('click', '按钮1')
// > clicked
// > clicked2 按钮1
em.$emit('change', '按钮1', 999)
// > changed:通过按钮1,改变成999
完整代码
class EventEmitter {
constructor() {
this.subs = Object.create(null)
}
$on (eventType, handler) {
this.subs[eventType] = this.subs[eventType] || []
this.subs[eventType].push(handler)
}
$emit (eventType, ...params) {
this.subs[eventType].forEach(handler => handler(...params))
}
}
const em = new EventEmitter()
function handleClick (params1) {
console.log('clicked', params1)
}
em.$on('click', handleClick)
em.$on('click', (params1) => {
console.log('clicked2', params1)
})
em.$on('change', (params1, params2) => {
console.log(`changed:通过${params1},改变成${params2}`)
})
em.$emit('click', '按钮1')
// > clicked
// > clicked2 按钮1
em.$emit('change', '按钮1', 999)
// > changed:通过按钮1,改变成999
todo
-
$off销毁已经注册的事件 -
$once注册一个只会触发一次的事件