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

我们知道vue3中移除了$on,$off 和 $once,所以不提供全局事件总线的能力了。并且官方也推荐使用mitt 或 tiny-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);

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

往期文章

专栏文章

相关推荐
丑陋小蚊子1 小时前
项目数据不能公开,就写清交付物
面试·求职招聘·大学生·简历·简历下载
2601_949950635 小时前
练题簿在线免费刷题 从资料整理到考前自测
面试·职场和发展·pdf·word·刷题
福兮说6 小时前
设计稿是 #4A7C6F,页面量出来是 #4B7C6F:HEX、HSL、透明度、canvas 来回转的七个坑
前端·javascript·css·canvas
时间的拾荒人6 小时前
Qt 多线程详解:从 QThread 到实战
开发语言·qt·面试
海绵宝宝转agent6 小时前
MySql高频面试八股开源笔记总结
mysql·面试·开源
郑州光合科技余经理7 小时前
海外版外卖加盟:总站与分站配送规则怎么分开管
java·开发语言·前端·后端·uni-app·php·ai编程
小呆呆6667 小时前
副业搞起来,小说,漫画,漫剧的成本优化思路
前端·后端·面试
凤城老人7 小时前
从 PyQt6 到 Electron:给 Edge TTS 做一个“多角色配音机“的踩坑手记
javascript·typescript·electron
Dovis(誓平步青云)8 小时前
浇水提醒刚弹出又消失,植物状态别只存一个百分比
开发语言·前端·javascript·pdf·ecmascript·电脑
lsc_blog8 小时前
远程和居家办公的经历,技术简历上怎么摆
面试·职场和发展·重构·pdf·求职招聘