• home > webfront > ECMAS > vue3 >

    vue2升级vue3: Event Bus 替代方案—— mitt

    Author:zhoulujun Date:

    Vue3 removed $on, $off and $once methods from the instance completely $emit is still a part of the existing API as it s used to trigger event handlers declaratively attached by a parent component

    在看 https://v3-migration.vuejs.org/breaking-changes/events-api.html

    在vue2里面

    In 2.x, a Vue instance could be used to trigger handlers attached imperatively via the event emitter API ($on, $off and $once). This could be used to create an event bus to create global event listeners used across the whole application:

    // eventBus.js
    
    const eventBus = new Vue()
    
    export default eventBus

    直接在项目使用 

    eventBus.$on('custom-event', () => {//TODO})

    eventBus.$emit('custom-event')

    但是,vue3移除了

    We removed $on, $off and $once methods from the instance completely. $emit is still a part of the existing API as it's used to trigger event handlers declaratively attached by a parent component.

    The event bus pattern can be replaced by using an external library implementing the event emitter interface, for example mitt or tiny-emitter.

    所以只能找第三方库,官方推荐有2个,特意对比了下

    mitt vs tiny-emitter

    https://www.npmtrends.com/tiny-emitter-vs-mitt

    WX20220615-144427.jpg

    mitt 和 tiny-emitter 对比分析

    共同点

    1. 都支持on(type, handler)、off(type, [handler])和emit(type, [evt])三个方法来注册、注销、派发事件

    不同点

    • emit

      1. 有 all 属性,可以拿到对应的事件类型和事件处理函数的映射对象,是一个Map不是{}

      2. 支持监听'*'事件,可以调用emitter.all.clear()清除所有事件

      3. 返回的是一个对象,对象存在上面的属性

    • tiny-emitter

      1. 支持链式调用, 通过e属性可以拿到所有事件(需要看代码才知道)

      2. 多一个 once 方法 并且 支持设置 this(指定上下文 ctx)

      3. 返回的一个函数实例,通过修改该函数原型对象来实现的

    更多参看:mitter事件派发---mitt和tiny-emitter源码分析https://juejin.cn/post/7022851362568454157


    看官方代码案例是tiny-emitter

    $emit 目前只能从子组件向父组件传值了,event Bus 只有借助第三方库了

    // eventBus.js
    import emitter from 'tiny-emitter/instance'
    export default {
      $on: (...args) => emitter.on(...args),
      $once: (...args) => emitter.once(...args),
      $off: (...args) => emitter.off(...args),
      $emit: (...args) => emitter.emit(...args)}

    具体参看:https://github.com/scottcorgan/tiny-emitter

    但是个人推荐 使用mitt

    mitt.js使用

    mitt 的用法和 EventEmitter 类似,通过 on 方法添加事件,off 方法移除,clear 清空所有。

    import mitt from 'mitt'
    
    const emitter = mitt()
    
    // listen to an event
    emitter.on('foo', e => console.log('foo', e) )
    
    // listen to all events
    emitter.on('*', (type, e) => console.log(type, e) )
    
    // fire an event
    emitter.emit('foo', { a: 'b' })
    // clearing all events
    emitter.all.clear()

    这里安利一下:《原生js实现事件监听类on/emit/off方法,Vue event事件机制解读

    mitt基于事件总线的订阅发布模式,任意组件之间都可以通信。provide/inject 在非组件中(一般用于子孙组件传值),就没法用了。

    如何用 event bus 或 mitt 實現跨元件傳遞資料

    在绝大多数情况下,不鼓励使用全局的事件总线在组件之间进行通信。虽然在短期内往往是最简单的解决方案,但从长期来看,它维护起来总是令人头疼。根据具体情况来看,有多种事件总线的替代方案:

    • props / emit 应该是父子组件之间沟通的首选。兄弟节点可以通过它们的父节点通信。

    • Provide 和 inject 允许一个组件与它的插槽内容进行通信。这对于总是一起使用的紧密耦合的组件非常有用。

    • provide/inject 也能够用于组件之间的远距离通信。它可以帮助避免“prop 逐级透传”,即 prop 需要通过许多层级的组件传递下去,但这些组件本身可能并不需要那些 prop。

    • Prop 逐级透传也可以通过重构以使用插槽来避免。如果一个中间组件不需要某些 prop,那么表明它可能存在关注点分离的问题。在该类组件中使用 slot 可以允许父节点直接为它创建内容,因此 prop 可以被直接传递而不需要中间组件的参与。

    • 全局状态管理,比如 Vuex



    具体到插件如何用呢?

    全局总线

    import { createApp } from 'vue';
    import App from './App.vue';
    import mitt from "mitt"
    
    const app = createApp(App)
    app.config.globalProperties.$mybus = mitt()

    vue2、vue3 ,个人都不推荐这种模式,即使要这种方式,推荐用:

    import { createApp } from "vue";
    import App from "./App.vue";
    
    import mitt from 'mitt';                  // Import mitt
    const emitter = mitt();                   // Initialize mitt
    
    const app = createApp(App);
    app.provide('emitter', emitter);          // ✅ Provide as `emitter`
    app.mount('#app');

    在子组件

    import { inject } from 'vue'
    
    export default {
      setup() {
        const emitter = inject("emitter"); // Inject `emitter`
        const mymethod = () => {
          emitter.emit("myevent", 100);
        };
        return {
          mymethod
        }
      }
    }

    任意组件都可以inject 出来,直接用。

    封装自定义事务总线文件 mybus.js

    创建新的 js 文件,在任何你想使用的地方导入即可。

    import mitt from 'mitt';
    type Events = {
      menuDrag?: DragEvent;
      menuDragend?: DragEvent;
    };
    
    const Bus = mitt<Events>();
    
    export default Bus;

    子组件使用

    onMounted(() => {
      Bus.on('menuDrag', (e: DragEvent) => {
        drag(e);
      });
      Bus.on('menuDragend', (e: DragEvent) => {
        dragend(e);
      });
    });


    推荐使用此种模式

    直接在组件里面导入使用

    因为分散式更方便管理和排查问题。

    import mitt from 'mitt'
    export default {
      setup (props) {
        const formItemMitt = mitt()
        return {
          formItemMitt
        }
      }
    }

    组合式API推荐这种模式


    不管那种方式,Vue 3 都建议在 beforeUnmount 移除监听(vue2在beforeDestroy移除)



    参考文章:

    Vue3.x 推荐使用 mitt.js https://juejin.cn/post/6973106775755063333

    vue3:兄弟组件,跨组件传值,事件总线的通信方式(mitt / tiny-emitter) https://blog.csdn.net/qq_42543244/article/details/123806588

    Vue 3 Event Bus with Composition API https://javascript.tutorialink.com/vue-3-event-bus-with-composition-api/




    转载本站文章《vue2升级vue3: Event Bus 替代方案—— mitt》,
    请注明出处:https://www.zhoulujun.cn/html/webfront/ECMAScript/vue3/8837.html