Libon

浏览器内置的 "EventBus"

2023/12/06 学习浏览器中内置的原生 EventBus 概念与使用方法 #JavaScript

ToC

对于已经有一些编程经验的人来说,对 “EventBus” 的概念肯定不陌生,从 Vue2 中使用 new Vue 再到 Vue3 中官方推荐的 mitt,还有 node 中的 events 模块,都是 EventBus 这个概念的实践,但其实我们每个前端程序员从很早就开始接触这个概念了,只是那时我们还不知道叫这个名字(就像在学设计模式的时候也会恍惚间发现,原来我那次优化后的代码叫xxx模式呀),它就是 DOM 中的事件系统,同时事件系统也是一个标准的发布订阅模式。addEventListener 对应着 on 方法,removeEventListener 则对应着 off 方法。

<button id="button">button</button>

<script>
  const button = document.querySelector('#button')

  button.addEventListener('click', alert)

  button.removeEventListener('click', alert)
</script>

以上示例代码将事件监听器对象限制为 button 元素,如果我们在框架开发中想要使用 EventBus 来实现夸组件通信的话,我们就可以将事件监听器对象调整为 document 或是 window

<!-- 组件A -->
<button id="button">button</button>

<script>
  const button = document.querySelector('#button')

  function onButtonClick() {
    document.dispatchEvent(new CustomEvent('toggle'))
  }

  button.addEventListener('click', onButtonClick)
</script>
<!-- 组件B -->
<output id="output"></output>

<script>
  const output = document.querySelector('#output')

  let count = 0

  function onToggle() {
    output.textContent = ++count
  }

  document.addEventListener('toggle', onToggle)
</script>

以下示例模拟了上述行为:

是的,我们实际上是使用了 dispatchEvent 加上 CustomEvent 来完成了自定义事件触发的过程,使用原生的 addEventListener 来完成添加观察者的行为。

使用原生方案实现的一个好处是不需要引入额外的库,不会增加项目体积,同时还兼容各种框架,但缺点是需要手动维护事件,并且只能在非SSR的情况下使用,除此之外,CustomEvent 在 IE 上使用也会有兼容性问题(万恶的IE)。

触发其他组件按钮中的方法

其实平时在框架开发中,偶尔也会遇到调用其他组件的方法,比如说A组件内的一个按钮点击以后需要触发和B组件中某个按钮点击以后执行的事件,我们就可以利用 HTML 中 <label /> 元素的 for 属性与目标按钮的 id 属性绑定:

<!-- 组件A -->
<label for="button">
  <!-- 需要注意的是,如果这里面不能使用 button 标签,否则 for 属性则会失效 -->
  <!-- 因为 label 默认会找最近的表单元素进行关联,如果内部有表单则不会使用 for 去寻找外部表单 -->
  <!-- 同时 组件B 中与 label 所关联的元素也需要是 <button /> 或 <input /> -->
  <span>组件A的按钮</span>
</label>
<!-- 组件B -->
<button id="button" onclick="javascript:alert(1);">组件B的组件</button>

以下示例模拟了上述行为:

以上。

CD ..