原生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);

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

往期文章

专栏文章

相关推荐
兰令水5 小时前
leecodecode【面试150】【2026.6.14打卡-java版本】
java·算法·面试
JustHappy11 小时前
古法编程秘籍(七):互联网到底是什么?把两台电脑怎么说话搞懂就够了
前端·后端·网络协议
老毛肚11 小时前
jeecg-boot-base-core 02 day
javascript·python
snow@li11 小时前
SEO-文章标题:写文章时候,分类+主标题+大纲+解释 作为标题 / 不点进去也知道全文覆盖什么 / 标题即架构
前端
kyriewen12 小时前
Git Commit 前自动修复代码风格?配置 Husky + lint-staged,从此 CR 只聊逻辑
前端·git·面试
小和尚同志12 小时前
AI 自动化测试探索(一):Playwright MCP
前端·人工智能·aigc
程序员二叉12 小时前
【JUC】ThreadLocal底层原理|内存泄漏|弱引用|跨线程传递方案
java·开发语言·面试·职场和发展·juc
程序员二叉12 小时前
【JUC】线程池全套深度详解|参数|流程|拒绝策略|调优|异常处理
java·开发语言·jvm·算法·面试·juc
老马识途2.012 小时前
在AI的帮助下理解spring的启动过程
java·前端·spring
徐小夕13 小时前
Loop Engineering 深度解析与实战指南(全网最全)
前端·算法·github