Skip to content
概念

发布-订阅模式(Publish-Subscribe Pattern)是一种消息范式,其中存在一个代理中心(通常称为发布-订阅系统或消息队列),在该系统中,发布者发布消息,而订阅者订阅这些消息。发布者和订阅者之间没有直接的耦合关系,它们通过代理中心进行通信,通过解耦发布者和订阅者之间的关系,实现了组件之间的松耦合通信,提高了系统的灵活性和可扩展性。 yuque_diagram (5).png 就上图来看,订阅者1、2 订阅了事件A,当发布者发布事件A时,中心通过查找订阅者,并通知订阅者1和订阅者2。

逻辑

实际开发过程中,我们经常接触发布-订阅模式,比如DOM的事件绑定、Vue的EventBus传递信息等。 让我们简单梳理一下逻辑: 首先,考虑中心存储的数据结构。因为我们需要处理多对多的关系,所以可以使用对象或者Map来存储关系。本次我们使用Map来存储关系。

typescript
_subscribers:Map<string, Fn[]>;

接下来,订阅者需要订阅对应的事件。为此,我们必须提供两个参数:事件名称和中心在接收到事件后通知订阅者要执行的操作。

typescript
//name就是事件名称,callback就是表示事件发生后要做的事情
onSubscribe:(name:string, callback:Fn)=>void

然后,发布者向中心发布一个事件。同样需要事件名称,当然还可以携带一些参数。

typescript
//发布者发布一个事件,name是事件名称,其他的为参数。
onPublish:(name:string, ...args:any[])=>void;

最后,中心通知对应的订阅者,事件发生了,并告诉它们该做什么。同时,也将发布者发布时携带的参数发送给订阅者。

实现
typescript
class GuPubSub  {
  //事件中心
  _subscribers:Map<string, Fn[]>

  constructor() {
    this._subscribers = new Map()
  }
  //订阅事件
  onSubscribe(eventName:string, callback:Fn):void {
    let callbacks = this._subscribers.get(eventName) || []
    callbacks.push(callback)
    this._subscribers.set(eventName, callbacks)
  }
  //发布事件
  onPublish(eventName:string, ...args) {
    // 中心根据事件名称 查找对应的订阅事件,然后执行
    let callbacks = this._subscribers.get(eventName)
    if (callbacks) {
      callbacks.map(callback => callback(...args))
    }
  }
  //只订阅一次
  once(eventName:string, callback:Fn) {
    let onceFn = (...args) => { 
      callback(...args)
      this.unsubscribe(eventName, onceFn)
    }
    this.onPublish(eventName, onceFn)
  }
  //移除订阅
  unsubscribe(eventName:string, callback:Fn) {
    let callbacks = this._subscribers.get(eventName) || []
    this._subscribers.set(eventName, callbacks.filter(callbackItem => callbackItem !== callback))
  }
}
在上面的代码中,once函数用于订阅者只需要订阅一次事件的情况。通过执行该函数,在执行完回调后会自动移除对该事件的订阅。