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