23种设计模式,前端常用的有几种?什么是事件总线?它属于哪种?

252 阅读4分钟

前言

  • 设计模式其实就是编程中的23种经典套路,它们在特定场景下能帮助我们编写出更加优雅和高效的代码。
  • 虽然设计模式对每位开发者来说都至关重要,但现有的书籍往往以Java为例进行讲解,这可能让我们这些“切图仔”感到不够友好。
  • 因此,我后续会写一篇专门针对我们这些“切图仔”的设计模式文章,并且会列出具体的应用场景,感兴趣的话关注一波再走:# 以 JavaScript 浅谈常见的几种设计模式
  • 今天主要讲的事件总线(eventBus),其实也是设计模式中的发布订阅模式
  • 发布订阅模式和观察者模式本质上是一样的,所以很多地方也就直接把两者看成一种设计模式了
    • 观察者模式是一个对象有多个依赖
    • 发布订阅模式其实是引入了一个中间层,来进行注册和通知
      • 其实 eventBus 就是实现了发布订阅模式
      • eventBus就是这个中间层,可以调用它的on方法进行注册(也就是订阅),调用它的emit方法进行通知(也就是发布)

事件总线的具体实现和使用方法

  • 手写事件总线的具体步骤

    • 其实核心思想就是当开发者给on方法传入“被监听事件名”和“事件触发要做的具体操作”之后,将它们存在一个对象中
    • 开发者会在“被监听事件”中调用emit方法,当“被监听事件”触发了之后,那么就会调用emit方法,我们就可以通过调用emit事件时传入的“被监听事件名”找到被存在对象中的“事件触发要做的具体操作”,然后对其调用即可
// 封装一个实现了事件总线具体操作的类
class zdEventBus {

  constructor() {
      this.events = {}
  }

  // 事件的监听方法
  on(eventName, eventFn) {
      let eventKey = this.events[eventName]
      // 因为考虑到一个“被监听事件名”可能会对应多个操作,所以将操作都放在一个数组中,最后遍历数组,执行操作即可
      if (!eventKey) {
          this.events[eventName] = []
      }

      this.events[eventName].push(eventFn)
  }

  // 时间的取消监听方法
  // 开发者如果想要删除某个“被监听事件名”对应的某个“事件触发要做的具体操作”,调用该方法即可
  off(eventName, eventFn) {
      let eventKey = this.events[eventName]
      if (!eventKey) return
      eventKey.splice(eventKey.indexOf(eventFn), 1)

      // 如果这个“被监听事件名”对应的数组已经为空了,那么就将这个key从对象中删掉
      if (eventKey.length === 0) {
          Reflect.deleteProperty(this.events, eventName)
      }
  }

  // 事件的发射方法
  emit(eventName, ...args) {
      let eventKey = this.events[eventName]
      if (!eventKey) return
      eventKey.forEach(fn => fn(...args));
  }

}
  • 事件总线的使用方法
    • 首先,拿到实现事件总线的类zdEventBus之后,在aside板块中通过它的实例对象调用它的on方法,需要传入两个参数,第一个参数代表要监听事件的名字,第二个参数则是,如果监听的事件被触发之后,要做的操作
    • 其次,就是在被监听的那个事件触发的时候,也就是在nav板块中btn的onclick事件被触发后,调用一下事件总线的实例对象的emit方法,使事件总线能够知道,这个事件现在被触发了
<body>

    <button class="nav-btn">按钮</button>

    <script>
        // 拿到事件总线这个类的实例对象
        const eventBus = new zdEventBus()  

        // aside板块
        // 因为在监听完成之后还要调用off方法取消监听,所以得将方法提取到外面,不能使用匿名函数
        const click1 = function(name, age, height) {
          console.log("nav-click发生了_01", name, age, height)
        }
        eventBus.on("nav-click", click1)
        eventBus.off("nav-click", click1)

        const click2 = function() {
            console.log("nav-click发生了_02")
        }
        eventBus.on("nav-click", click2)
        eventBus.off("nav-click", click2)


        eventBus.on("footer-click", function() {
            console.log("footer-click发生了_01")
        })

        console.log(eventBus.events)


        // nav板块
        const btnEl = document.querySelector(".nav-btn")
        btnEl.onclick = function() {
            console.log("btnEl发生点击了")
            eventBus.emit("nav-click", "Judy", 18, 1.88)
        }
    </script>

</body>

总结

  • 其实说白了,on方法就是将要监听的事件名称以及对应的事件处理函数交给 eventBus 进行存储
  • 然后在需要发出事件的地方,就调用 emit 方法并将要触发的事件名称传入
    • 在 emit 方法中其实就是在根据传入的事件名称去调用对应的事件处理函数