恙髡 发表于 2025-6-9 09:13:17

发布订阅模式的TS实现

简介

发布订阅模式是一种常用的用于解耦的模式。
它和观察者模式的区别在于:

[*]观察者模式:被观察者需要维护一个观察者的集合;
[*]发布订阅模式:通信双方互相不知道对方的存在,通过第三方事件总线进行通信。
发布订阅模式在前端领域很常见,例如:

[*]Vue 框架中组件的$on和$emit方法;
[*]Node.js 中 EventEmitter 中的 on和 emit 方法。
图示:

[*]订阅者通过on方法注册事件:

[*]发布者通过emit触发回调列表:

发布者和订阅者双方不知道各自的存在,它们仅通过Event Bus进行通信。
实现

事件总线最基本的两个方法是 on 和 emit 。
常见的设计还有两个方法是 off 和 once,off 用于注销事件,once 是 on 的特例,表示仅订阅一次。
函数签名

type CallbackFn = (...args: any[]) => void;

on(eventName: string, callback: CallbackFn): void;
emit(enentName: string, ...args: any): void;
off(eventName: string, callback: CallbackFn): void;
once(eventName: string, callback: CallbackFn): void;实现思路

在事件总线中需要建立起事件名到回调集合的映射:

[*]当on时,将回调添加到指定事件名的回调集合中;
[*]当emit时,遍历指定时间名的回调集合,依次执行其中的回调函数;
回调集合可以使用Set实现,也可以使用数组实现。
由于on的实现需要做去重,建议使用Set,比较方便。
once的实现:
once 可以基于 on 和 off 实现,先使用 on 注册,执行回调之后就执行 off 注销,从而实现仅触发一次。
代码

使用 TypeScript 实现。
首先声明一个回调函数的类型,简化后续代码:
type CallbackFn = (...args: any[]) => void;然后是声明 EventBus 类,成员属性中使用对象建立起 “事件名与回调集合” 的映射关系:
class EventBus{
    events: Record<string, Set<CallbackFn>> = {};

    constructor(){}
       
    on(eventName: string, callback: CallbackFn){ /* ... */ }
    emit(eventName: string, ...args: any[]){/* ... */}
    off(eventName: string, callback: CallbackFn){/* ... */}
    once(eventName: string, callback: CallbackFn){/* ... */}
}on
on(eventName: string, callback: CallbackFn){
    if(!this.events){
      this.events = new Set();
    }
    this.events.add(callback);
}

[*]如果事件名不存在,则要初始化创建一个 Set 用于记录。
[*]如果事件名存在,则直接将回调添加到 Set 中。
这段代码可以通过 短路运算符 简化:
on(eventName: string, callback: CallbackFn){
    (this.events ??= new Set()).add(callback);
}emit

遍历回调集合就
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 发布订阅模式的TS实现