事件总线Mitt使用非常简单,本篇随笔介绍在Vue3+TypeScript 前端项目中使用的一些场景和思路。我们在Vue 的项目中,经常会通过emits 触发事件来通知组件或者页面进行相应的处理,不过我们使用事件总线Mitt来操作一些事件的处理,也是非常方便的。

Mitt 的GitHub官网地址如下所示:
https://github.com/developit/mitt
, 它的安装和其他插件一样,我们不再赘述,只讲述它的如何使用。

Mitt
具有以下优点:

  • 零依赖、体积超小,压缩后只有
    200b
  • 提供了完整的
    typescript
    支持,能自动推导出参数类型。
  • 基于闭包实现,没有烦人的
    this
    困扰。
  • 为浏览器编写但也支持其它
    javascript
    运行时,浏览器支持
    ie9+
    (需要引入
    Map

    polyfill
    )。
  • 与框架无关,可以与任何框架搭配使用。
Mitt 只是提供了几个简单的方法,如on,off, emit 等基础的几个函数。
在JS中我们使用的话,不需要类型化事件的类型,如下代码所示。
import mitt from 'mitt'const emitter=mitt()//订阅一个具体的事件
emitter.on('foo', e => console.log('foo', e) )//订阅所有事件
emitter.on('*', (type, e) =>console.log(type, e) )//发布一个事件
emitter.emit('foo', { a: 'b'})//根据订阅的函数来取消订阅
functiononFoo() {}
emitter.on(
'foo', onFoo) //listen emitter.off('foo', onFoo) //unlisten //只传一个参数,取消订阅同名事件 emitter.off('foo') //unlisten //取消所有事件 emitter.all.clear()

而我们如果在Vue3 + TypeScript 环境中使用的话,就需要类型化事件的类型,已达到强类型的处理目的。

import mitt from "mitt";

type Events
={
foo: string;
bar: number;
};
//提供泛型参数让 emitter 能自动推断参数类型 const emitter = mitt<Events>();//'e' 被推断为string类型 emitter.on("foo", (e) =>{
console.log(e);
});
//ts error: 类型 string 的参数不能赋值给类型 'number' 的参数 emitter.emit("bar", "xx");//ts error: otherEvent 不存在与 Events 的key中 emitter.on("otherEvent", () =>{//});

在前端项目使用的时候,我们在utils/mitt.ts中定义默认导出的mitt对象,如下代码所示。

//utils/mitt.ts
import mitt, { Emitter } from'mitt';//类型
const emitter: Emitter<MittType> = mitt<MittType>();//导出
export default emitter;

在其中的MittType类型,可以单独文件放置TypeScript的预定义文件目录中,如types/mitt.d.ts

而我们在使用的时候,直接导入该对象就可以了,如下代码所示。

declare type MittType<T = any> ={
openSetingsDrawer
?: string;
restoreDefault
?: string;
setSendColumnsChildren: T;

..................
//省略其他事件类型 noticeRead: number;//消息已读事件 lastAddParentId?: string | number;//新增记住最后的父信息 };

例如我们定义一个更新和记住父菜单的Mitt 事件,在页面加载完毕的时候监听事件,在页面退出的时候关闭事件即可,如下代码所示是在菜单列表页面中处理的。

<script lang="ts" setup name="sysMenu">import { onMounted, onUnmounted, reactive, ref } from'vue';
import mittBus from
'/@/utils/mitt';
......
onMounted(async ()
=>{
handleQuery();

mittBus.on(
'submitRefresh', () => {
handleQuery();
});
mittBus.on('lastAddParentId', (pid) => {
state.lastAddParentId = pid as string;//记住最后的父菜单ID
});
});

onUnmounted(()
=>{
mittBus.off(
'submitRefresh');
mittBus.off('lastAddParentId'
);
});
</script>

在新增菜单的时候我们触发对应刷新事件
submitRefresh
,以及触发选择的父记录ID的事件
lastAddParentId
,这样就可以做相应的处理了。

例如在菜单的编辑子控件页面中,我们触发对应的事件逻辑代码如下所示。

//关闭弹窗
const closeDialog = () =>{
mittBus.emit(
'submitRefresh');
state.isShowDialog
= false;
};
//提交 const submit = () =>{
ruleFormRef.value.validate(async (valid:
boolean) =>{if (!valid) return;if (state.ruleForm.id != undefined && state.ruleForm.id > 0) {
await menuApi.update(state.ruleForm);
}
else{
await menuApi.add(state.ruleForm);
//记住最后的菜单 mittBus.emit('lastAddParentId', state.ruleForm.pid);
}
closeDialog();
});
};

如果为了减少每次重复的导入mitt,也可以把它全局挂载到变量中,统一入口进行访问,详细可以参考随笔《
在基于vue-next-admin的Vue3+TypeScript前端项目中,为了使用方便全局挂载的对象接口
》处理即可。

const $u: $u_interface ={
message,
test,
util,
date,
crypto,
base64,
$t: i18n.global.t,
fun: commonFunction(),

cloneDeep,
debounce,
throttle,
mitt
};
//安装$u组件到app上 import type { App } from 'vue';
export
default{
install(app: App
<Element>) {//挂载全局 app.config.globalProperties.$u =$u;
}
};

标签: none

添加新评论