vue3跨组件(多组件)通信:事件总线【Event Bus】
★推荐方案:使用 `events` npm库;
可用范围:vue、react、angular等任何框架都可使用;且使用方式完全一致;
本文仅介绍、讲解对web页面端项目的常用API;通过events实现事件总线功能;
event库概述:本次所使用到的库为通用库,若在node环境使用则无需npm安装,本身自带的;浏览器环境下使用才需要npm安装。
events库是从 Node.js 上移植Events模块的功能,因此可用的API完全一致(除了仅限在node环境下使用的API);
若想进一步深入,去看node官网的event文档即可。(注意版本差异!根据npm文档的描述,该库目前并未同步最新Node里对应的events模块)
在VUE3的官方文档中描述到:【平级组件或是跨越多层嵌套的组件间通信,应使用一个外部的事件总线,或是使用一个全局状态管理方案】。全局状态管理就则是像pinia这种,本文就主要讲述事件总线;
VUE2的话直接用官方API提供的即可,当然如果vue2的相关API实现的事件总线不满足需求,那自然也是推荐使用events库的!
events库使用方式:
1、安装
npm install events
2、引入使用、封装
import { EventEmitter } from 'events';
// 大家根据各自业务需求自行封装对应风格的事件总线模块;
export const emitter = new EventEmitter();
// 创建多个事件总线,互不干扰。
export const emitter2 = new EventEmitter();
这样我们就创建好了一个简单的事件总线实例,也可以创建多个事件总线
3、注册监听器:eventEmitter.on() 与监听函数的this指向;
类型:on(eventName: string | symbol, listener: (...args: any[]) => void): this;
别名:emitter.addListener(eventName, listener)
import { emitter,emitter2 } from '@/utils/events'
// 可接收多个参数,若emit触发该监听函数时未传参则是undefined
// 第一个参数类型:eventName: string | symbol
// 第二个参数类型: listener: (...args: any[]) => void
emitter.on('test1',function (val,val2,val3){
console.log(val,val2,val3,'可接受多个参数!');
console.log(this,'该this指向为emitter实例,若监听函数为箭头函数,则该处this指向不用我解释了吧?这是基础啊');
})
emitter.on('test1',function (){
console.log('可多次注册同名的test1监听函数;注意事项看 《7、监听数量相关内容》 ');
})
// 按上一步所示,不同事件总线实例创建监听器时eventName同名也不会互相影响;
emitter2.on('test1',(val)=>{
console.log(val);
})
function emitTest() { // 标准写法,这样才可off卸载监听函数;上面2个例子只是展示功能作用;
console.log('葬送的芙莉莲');
}
emitter.on('emitTest',emitTest)
emitter.off('emitTest',emitTest)
另外,EventEmitter 按照注册的顺序同步地调用所有监听器。 这确保了事件的正确排序,并有助于避免竞争条件和逻辑错误。
4、触发监听器事件:eventEmitter.emit()
emit类型为:emit(eventName: string | symbol, ...args: any[]): boolean;
下面代码中的 `emitter.emit('test1',1,'向监听的函数传入多个参数') ` 表示触发所有 test1 的监听函数,并传入后面那2个参数。
返回值:如果事件有监听器,则返回 true
,否则返回 false
。
import { emitter,emitter2 } from '@/utils/events'
emitter.emit('test1',1,'向监听的函数传入多个参数')
emitter2.emit('test1','来自 emitter2 的问候.')
// boolean返回值表示未触发该监听函数;下面两个emit返回的结果都是false
const has = emitter2.emit('emitTest','返回false') // 就算on创建过监听函数,off后此时触发不到也是返回false
const has2 = emitter2.emit('test666','返回false')
5、仅触发一次监听器事件:eventEmitter.once()
类型:once(eventName: string | symbol, listener: (...args: any[]) => void): this;
使用 eventEmitter.once() 方法,可以注册一个监听器,该监听器最多为特定事件调用一次。 一旦事件被触发,则监听器就会 先被注销 然后再调用该监听函数。
import { emitter,emitter2 } from '@/utils/events'
let countt = 0
emitter.once('test2',function () {
++countt
console.log(countt);
})
import { emitter } from '@/utils/events'
function demo (){
let has = emitter.emit('test2')
console.log(has);
}
demo(); // 第一次Log是true,emit.once执行一次后就会销毁
demo(); // 销毁后再执行自然是找不到该监听函数的,所以返回false
6、卸载/销毁 监听器 emitter.off()
类型:off(eventName: string | symbol, listener: (...args: any[]) => void): this;
别名:emitter.removeListener()
从名为 eventName 的事件的监听器数组中移除指定的 listener。
on、once创建的监听器都可用off销毁;
import { emitter } from '@/utils/events'
function emitTest() {
console.log('葬送的芙莉莲');
}
emitter.on('emitTest',emitTest)
emitter.off('emitTest',emitTest)
6、监听EventEmitter的 on、off 事件;
import { EventEmitter } from 'events';
export const emitter = new EventEmitter();
// 在将监听器添加到其内部监听器数组之前,EventEmitter 实例将触发自身的 'newListener' 事件。
// 以此来监听 `事件总线` 的添加事件;
emitter.on('newListener', (eventName, listener) => {
console.log(eventName, listener,'这是添加');
});
// 同理,监听删除事件
emitter.on('removeListener', (eventName, listener) => {
console.log(eventName, listener,'这是删除');
});
7、监听数量相关内容:
- 默认监听同一事件名的最大数量为10,若是注册超过10个以上则会抛出警告。
-
emitter.getMaxListeners() 获取emitter事件总线实例可注册监听器同一事件名的最大数量,默认为10;
-
通过emitter.setMaxListeners(8) 来更改限制,传入
Infinity
(或0
)则表示不限制;【虽然即使限制了,但超出也只是抛出警告,还是会正常注册该监听函数。抛警告是便于防止内存泄漏问题】;另外setMaxListeners跟defaultMaxListeners无关,setMaxListeners 修改了并不会更改 defaultMaxListeners ,简单来说就是2个属性名,set修改的是 _maxListeners 属性值,该属性有值时以 _maxListeners 为优先,没值则是 -
emitter.defaultMaxListeners 返回默认监听最大数量,但好像没啥用,返回undefined,用getMaxListeners则能拿到官方说的10。
官方说defaultMaxListeners不要更改!不然会影响所有实例。虽然我验证了下改了也没用,但官方说啥就是啥,你别改就完事了,而且改这个属性也是不规范的做法。
import { EventEmitter } from 'events';
export const emitter = new EventEmitter();
console.log(emitter.defaultMaxListeners);
// emitter.setMaxListeners(3)
console.log(emitter.getMaxListeners());
emitter.on('ceshi1',demo)
emitter.on('ceshi1',demo)
emitter.on('ceshi1',demo)
emitter.on('ceshi1',demo)
emitter.on('ceshi1',demo)
emitter.on('ceshi1',demo)
emitter.on('ceshi1',demo)
emitter.on('ceshi1',demo)
emitter.on('ceshi1',demo)
emitter.on('ceshi1',demo)
emitter.on('ceshi1',demo) // 默认10个,此时再注册则会报warn提醒你
emitter.on('ceshi1',demo)
8、eventNames:返回事件总线里已注册了的所有监听器事件名(eventName)。
类型:eventNames(): Array<string | symbol>;
多次同名注册的也只返回一个eventName给数组。
查看某监听器事件名注册次数看下面的内容 9。
import { emitter } from '@/utils/events'
console.log(emitter.eventNames())
9、listenerCount:获取监听名为 eventName
的事件的监听器数量。
类型:listenerCount(eventName: string | symbol): number;
import { EventEmitter } from 'events';
export const emitter = new EventEmitter();
emitter.on('ceshi1',demo)
emitter.on('ceshi1',demo)
emitter.on('ceshi1',demo)
console.log(emitter.listenerCount('ceshi1')); // log: 3