事件总线Mitt使用非常简单 ,本篇随笔介绍在Vue3+TypeScript 前端项目中使用的一些场景和思路 。我们在Vue 的项目中 ,经常会通过emits触发事件来通知组件或者页面进行相应的处理 ,不过我们使用事件总线Mitt来操作一些事件的处理 ,也是非常方便的 。
Mitt 的GitHub官网地址如下所示:https://github.com/developit/mitt, 它的安装和其他插件一样 ,我们不再赘述 ,只讲述它的如何使用 。
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 })
// 根据订阅的函数来取消订阅
function onFoo() {}
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;
}
};
声明:本站所有文章 ,如无特殊说明或标注 ,均为本站原创发布。任何个人或组织,在未征得本站同意时 ,禁止复制 、盗用 、采集 、发布本站内容到任何网站 、书籍等各类媒体平台 。如若本站内容侵犯了原著者的合法权益 ,可联系我们进行处理 。