原生js如何实现全局事件总线

我们知道vue3中移除了$on$off$once,所以不提供全局事件总线的能力了。并且官方也推荐使用mitttiny-emitter来做事件总线功能。

那么这篇文章就是介绍如何使用js原生提供的自定义事件实现组件间的通信的。即类似于全局事件总线的类库的。

首先我们先了解一下EventTarget。我们可以通过EventTarget显示的创建一个事件目标对象用于提供全局定义和派发事件。

js 复制代码
// 全局事件目标对象
const eventTarget = new EventTarget();
export default eventTarget

类似于使用mitt类库定义全局派发对象。

js 复制代码
import mitt from 'mitt'

const emitter = mitt()
export default emitter

然后我们可以通过CustomEvent来定义一个派发事件对象。注意,我们一般使用全局事件总线发送事件时,都是想要传递一些信息的,我们使用CustomEvent就可以定义一些数据进行传递。

CustomEvent参数

  • 参数一:派发事件的名称,用于事件监听器获取。
  • 参数二:用于提供派发事件的配置。
    • detail:派发时传递的参数。
    • bubbles: 事件是否可以冒泡。这个主要是用于document去做全局派发对象时,可以在任何元素上触发起作用。利用事件冒泡的原理。我们使用EventTarget作为全局派发对象就无所谓了。
    • cancelable: 事件是否可以取消。我们一般可以通过event.cancelable属性来判断当前事件是否可以取消,即通过event.preventDefault()去取消默认行为。
js 复制代码
// 另一个组件派发事件
const customEvent = new CustomEvent("foo", { detail: "详细数据" });
eventTarget.dispatchEvent(customEvent);

类似于mitt中的emit

js 复制代码
emitter.emit('handleIsResponseInput', {
    isDialog: true,
    commentId,
})

然后我们可以使用CustomTarget全局事件目标对象监听addEventListener当前派发的事件,获取派发数据(可以通过event.detail中拿到数据),进行对应的逻辑处理。并且我们可以通过removeEventListener来移除自定义事件。

js 复制代码
const fn = (event: Event) => {
  // event.detail中可以拿到传递的数据。
  console.log("event======", event)
}
eventTarget.addEventListener("foo", fn)

eventTarget.removeEventListener("foo", fn)

类似于mitt中的on

js 复制代码
emitter.on('handleIsResponseInput', (params) => {
  // 点击回复,打开对话框
  dialogVisible.value = params.isDialog
  commentId.value = params.commentId
})

以上就是通过原生js提供的自定义事件API去完成事件派发。如果是简单的使用,我们可以直接这种方式进行组件通信即可。但是在项目中尽量不要使用事件总线进行数据传递,因为使用起来比较混乱。

还有几种方式可以去定义自定义事件:

通过Event去定义一个事件对象

js 复制代码
// 注意:一般我们自定义事件都会传递一些数据,这种不能传递数据的,使用场景不大。
// Event不能传递自定义数据。参数二:bubbles: 是否可冒泡 cancelable: 是否可被取消 composed: 是否会在影子 DOM 根节点之外触发侦听器。(用于具有自定义元素)
const event = new Event("customEvent", {bubbles: true,cancelable: false, composed: false})
document.addEventListener("customEvent", (event) => {
  console.log("event", event)
})
// 触发在定义之后 (需要定义事件冒泡才可以在其他元素上触发。)即bubbles为true
div.dispatchEvent(event)

通过document.createEvent创建一个事件对象,并且通过initEvent()方法去初始化该事件对象 , 用这种方式初始化事件必须是由 Document.createEvent() 方法创建的实例。本方法必须在事件被触发之前调用(用EventTarget.dispatchEvent()调用)。事件一旦被调用,便不再做其他任何事。

js 复制代码
const elem=document.querySelector('#div');

const event=document.createEvent('Event');

// 必须进行初始化,否者无法派发,并且在派发之前初始化
event.initEvent('foo',true,true);

// 监听
document.addEventListener('foo',function(e) {
  console.log(e);
},false);

// 使用目标对象去派发事件,可以是元素节点/事件对象
// div派发的事件,document可以监听到。因为开启了事件冒泡
elem.dispatchEvent(event);

上面这两种定义自定义事件的方法,在派发事件时不能传递额外数据,所以用处不大,知识这里总结一下。

往期文章

专栏文章

相关推荐
掘金者阿豪10 分钟前
把业务数据变成共享仪表盘:Metabase可视化与远程访问实践
前端·后端
kyriewen30 分钟前
折腾了半年 AI 编程工作流,最后发现效率瓶颈是桌上那块屏幕
前端·javascript·ai编程
蜗牛前端1 小时前
codex 全流程开发上线的高颜值礼簿小程序
前端·微信小程序
大龄秃头程序员2 小时前
我在图文流 App 里落地双层缓存、弱网降级与 OOM 治理
前端
老王以为2 小时前
React Renderer 分离的多平台架构
前端·react native·react.js
hunterandroid2 小时前
Kotlin Coroutines 与 Flow:让异步任务更清晰
前端
Bigger2 小时前
从零搭建 AI 代码审查服务:一份前端也能看懂的 Python 学习笔记
前端·ci/cd·ai编程
lichenyang4533 小时前
JSAPI、NAPI、Biz、Imp:ASCF Demo 如何真正调用系统能力和 C++ 能力
前端
自由路飞3 小时前
RAG 混合检索深挖:BM25 和向量分数为什么不能直接相加?
面试
lichenyang4533 小时前
IPC、JSVM、UIThread、libuv:ASCF 架构图里最容易混的几个词
前端