好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

在Vue3+TypeScript 前端项目中使用事件总线Mitt

在Vue3+TypeScript 前端项目中使用事件总线Mitt

事件总线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'  })

  //   根据订阅的函数来取消订阅 
 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;
  }
}; 

 

 

 

查看更多关于在Vue3+TypeScript 前端项目中使用事件总线Mitt的详细内容...

  阅读:78次