mitt的使用与原理

1,685 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第8天,点击查看活动详情

一、mitt的介绍

Mitt 是为浏览器设计的,但适用于任何 JavaScript 运行时。它没有依赖项,支持 IE9+。体积小,小于200字节。在vue3中代替了vue2EventBus

二、使用

导入

npm install --save mitt

然后在main.ts的引入模块,创建实例并导出,这里是在untils文件中单独创建untils.ts文件

// 组件通信
import mitt from 'mitt'

export const emitter = mitt()

child组件传值

这里在组件中单独创建child1,child2文件

image.png

创建MittDemo文件,将两个组件引入进去

image.png

现在单独处理两个文件

传递信息

child1传递参数到child2,需要导入emitter实例,调用emit方法,传递事件和方法

// vue
<div>
    child-1
    <h2>child1组件</h2>
    <button @click="child1Emitter">emitter事件</button>
</div>

// ts

import { emitter } from '@/utils/until'
const child1Emitter = () => {
  emitter.emit('child2', '这是从child1传过来的参数')
}

child2页面监听该方法

import { emitter } from '@/utils/until'
emitter.on('child2', (e) => {
  console.log(e)
})

这里,在child1child2 都写了发送和监听方法

页面如下

image.png

测试

点击child1组件的事件 image.png 点击child2组件的事件

image.png

事件的调用

只需要在输出的位置,写需要调用的方法就可以了,参数也是从组件中传递过来的参数

点击child1按钮,组件child2响应

image.png

点击child2按钮,组件child1响应

image.png

父子组件通信

父组件调用子组件

在父组件导入实例,调用两个子组件的方法

// vue
<button @click="MittMain">mitt-Main主页事件</button>
 
// ts
import { emitter } from '@/utils/until'
const MittMain = () => {
  emitter.emit('child1', 'mittMain')
  emitter.emit('child2', 'mittMain')
}

效果如下

image.png

子组件调用父组件

在子组件child1child2值都已经写了这个时间,这里只在MittDemo页面写下监听

这里*是指监听所有事件

emitter.on('*', (e) => {
  console.log('主页面Mitt-main', e)
})

image.png

image.png

返回的参数是事件名

如果想拿到具体的方法的参数,那么需要写明这个事件名称

如下

emitter.on('child1', (e) => {
  console.log('主页面Mitt-main', e)
})

emitter.on('child2', (e) => {
  console.log('主页面Mitt-main', e)
})

image.png

事件的注销与清除

删除给定类型的事件处理程序

emitter.off('child1', '删除')

清空所有事件

emitter.all.clear()

三、源码解读

这里先不说这个复杂的ts定义,只解读方法

on事件

  • 通过get拿对应的值,如果handlers为真,则将时间添加到监听队列
  • 不存在,则在n里面设置键值对
on<Key extends keyof Events>(type: Key, handler: GenericEventHandler) {
    const handlers: Array<GenericEventHandler> | undefined = all!.get(type);
   if (handlers) {
      handlers.push(handler);
    } else {
	all!.set(type, [handler] as EventHandlerList<Events[keyof Events]>);
    }
},

off事件

  • 删除订阅消息的队列
  • get判断值是否存在,如果存在,那么就直接通过splice()删除, 有两个值一个通过indexOf拿下标,第二个是要删除的数量
  • >>> 表示无符号位移 它把无符号的 32 位整数所有数位整体右移。对于无符号数或正数右移运算,无符号右移与有符号右移运算的结果是相同的。
  • 对于负数来说,无符号右移将使用 0 来填充所有的空位,同时会把负数作为正数来处理,所得结果会非常大所以,使用无符号右移运算符时要特别小心,避免意外错误。
off<Key extends keyof Events>(type: Key, handler?: GenericEventHandler) {
    const handlers: Array<GenericEventHandler> | undefined = all!.get(type);
    if (handlers) {
        if (handler) {
            handlers.splice(handlers.indexOf(handler) >>> 0, 1);
        } else {
            all!.set(type, []);
        }
    }
},

emit 事件

  • 这里有两种情况,一种是发送type类型的时间,一种是*事件,
  • 通map循环依次执行事件,这里使用了slice,返回一个新数组,原数组不会改变
emit<Key extends keyof Events>(type: Key, evt?: Events[Key]) {
    let handlers = all!.get(type);
    if (handlers) {
        (handlers as EventHandlerList<Events[keyof Events]>).slice().map((handler) => { handler(evt!); });
    }

    handlers = all!.get('*');
    if (handlers) {
        (handlers as WildCardEventHandlerList<Events>).slice().map((handler) => { handler(type, evt!); });
    }
}

暂时就这么多,有问题或者错的东西,请留言,谢谢!!!