事件匯流排Mitt使用非常簡單,本篇隨筆介紹在Vue3+TypeScript 前端專案中使用的一些場景和思路。我們在Vue 的專案中,經常會通過emits 觸發事件來通知元件或者頁面進行相應的處理,不過我們使用事件匯流排Mitt來操作一些事件的處理,也是非常方便的。
Mitt 的GitHub官網地址如下所示:https://github.com/developit/mitt, 它的安裝和其他外掛一樣,我們不再贅述,只講述它的如何使用。
Mitt
具有以下優點:
200b
。typescript
支援,能自動推匯出引數型別。this
困擾。javascript
執行時,瀏覽器支援ie9+
(需要引入Map
的polyfill
)。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; } };