详解发布订阅模式

116 阅读2分钟

关于发布订阅模式

发布订阅模式应该算得上是前端框架中最常见的一种设计模式,vue的响应式原理,axios的取消发送等都使用到了发布订阅模式。
vue的响应式原理可以看我这篇文章[juejin.cn/post/716122…]

什么是发布订阅模式?

发布订阅模式是一种一对多的依赖关系,当一个状态发生改变时,所有依赖他的对象都得得到通知

结合现实举例

很多人去超市买东西,但是东西还没到货,老板说,你们先把自己的电话号码写本子上吧,到时候货到了我给你们打电话
很多人=>订阅者
老板=>发布者
把自己电话写本子上=>收集订阅者信息的过程
本子=>用于存放订阅者信息
打电话通知=>发布消息

发布订阅模式有哪些作用?

1.异步编程,在js中,我们无法获知一个异步任务什么时候完成,这时候可以在任务结束后发布消息,使外部订阅者获得通知,从而判断异步任务已完成
2.降低两个对象之间耦合度

发布订阅模式的应用

1.DOM事件

btn.addEventListener( 'click', function(){
     alert(2);
}, false );

user.click();    // 模拟用户点击

在js中,事件的绑定就利用到了发布订阅模式,给按钮绑定一个事件,当事件被触发时,通知该函数

2.自定义模式

// 发布者Dep
class Dep {
  constructor() {
    this.dep = {}
  }
  //收集依赖
  listen(key, call) {
    if (!this.dep[key]) {
      this.dep[key] = []
    }
    this.dep[key].push(call)
  }
  // 通知订阅者
  trigger() {
    const key = Array.prototype.shift.call(arguments)
    const fns = this.dep[key]
    if (!fns || fns.length == 0) {
      return false
    }
    for (let i = 0; i < fns.length; i++) {
      fns[i].apply(this, arguments)
    }
  }
  // 取消订阅事件
  remove(key, fn) {
    const fns = this.dep[key]
    if (!fns) {
      return false
    }
    if (!fn) {
      // 如果没有传入具体fn,则取消该key对应的所有fn
      this.dep[key] = null
    } else {
      const index = fns.indexOf(fn)
      this.dep[key].splice(index, 1)
    }
  }
}

注:在项目中使用发布订阅模式,可以降低项目模块间的耦合度

必须先订阅再发布吗?

上面所说的所有情况都是先订阅再发布,那么可不可以先发布再订阅呢?答案是可以的,有的时候我们订阅者可能还没加载好,但是发布者就已经发布通知了。因此为了让订阅者不错过接收消息,我们可以创建一个存放离线事件的堆栈,当事件发布的时候我们将发布事件用函数包起来,存放到堆栈中,当订阅者准备好后,可以重新遍历堆栈,从而实现消息的重新发布。

发布订阅模式的优缺点

优点

1.时间上的解耦
2.对象之间的解耦

缺点

消耗时间和内存:当订阅一个消息,但是该消息的发布者始终没有发布消息,该订阅者则会始终存放在内存中

参考: 《javascript设计模式与开发实践》