Skip to content
On this page

封装 mitt.js

mitt 是什么

  简单的说 mitt 就是一个全局的总线程,在 vue2.0 中,我们经常会使用EventBus去处理问题,这时候你会说啥是总线程呢。更简单的说,其实就是发布订阅事件。

EventBus 的简易源码

javascript
class Bus {
  constructor() {
    this.callback = {};
  }
  /**
   * @description: 发布事件
   * @param {*} name 名称
   * @param {*} fn 方法
   */
  $on(name, fn) {
    this.callback[name] = this.callback[name] || [];
    this.callback[name].push(fn);
  }
  /**
   * @description: 订阅器-接收器
   * @param {*} name 方法的名称
   * @param {*} args 入参
   */
  $emit(name, args) {
    if (this.callback[name]) {
      this.callback[name].forEach((cb) => {
        cb(args);
      });
    }
  }
}

// 挂载在 vue 的原型上
Vue.prototype.$bus = new Bus();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

miit 的使用

安装

npm install --save mitt
1

通常的使用

javascript
import mitt from "mitt";

const emitter = mitt();

const TOPIC = "topic";

// 订阅
emitter.on(TOPIC, (data) => {
  console.log(data);
});

// 发布事件
emitter.emit(TOPIC, { a: "b" });

// 取消订阅
emitter.off(TOPIC, onFoo);

// 清空所有的事件
emitter.all.clear();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

问题与痛点

命名问题

  每一次都需要去思考不重名,一旦使用重名,将很难定位问题

销毁事件

  事件都挂载到了总线程上,你需要思考如何去销毁掉订阅的事件,避免不必要的开销

为什么不去用 store 去处理问题

  所有的问题存在则是必然,可为什么是必然呢?做一个假设,你现在处理一个表格数据的刷新,你是希望把表格数据放在 store 中还是放在业务组件中吗?很显然业务组件中,可以让代码的逻辑更加要区分,而不是把所有的数据,一股脑的放入 store 处理,这样代码的单一职责原则才能更好的体现。

hook 封装

useEventbus.ts

typescript
import { onUnmounted } from "vue";
import mitt from "mitt";

type IUseEventbus = {
  customEmit: (eventName: string) => void;
  customOn: (eventName: string, callback: () => void) => void;
  toRefreshTable: () => void;
  refreshTable: (callback: () => void) => void;
};

const emitter: mitt.Emitter = mitt();

/**
 * @description: 自定义触发器
 * @param {*} eventName 名称
 */
const customEmit = (eventName: string) => {
  emitter.emit(eventName);
};

/**
 * @description: 自定义接收器
 * @param {*} name 名称
 * @param {*} callback 回调的函数
 */
const customOn = (eventName: string, callback: () => void) => {
  emitter.on(eventName, () => callback());
};

/**
 * @description: 通知刷新表格数据
 */
const toRefreshTable = () => {
  emitter.emit("refreshTable");
};

/**
 * @description: 刷新表格数据
 * @param {*} callback 回调的函数
 */
const refreshTable = (callback: () => void) => {
  emitter.on("refreshTable", () => callback());
};

/**
 * @description: 导出useEventbus
 */
export const useEventbus = (): IUseEventbus => {
  // 销毁的事件
  onUnmounted(() => {
    // 清空所有的事件,避免多组件互相清理
    emitter.all.clear();
  });

  return {
    customEmit,
    customOn,
    toRefreshTable,
    refreshTable,
  };
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

main.vue - 主组件

html
<script lang="ts" setup>
  import { onMounted } from "vue";
  import { useEventbus } from "@/hooks/useEventbus";

  const eventbus = useEventbus();

  onMounted(() => {
    // 订阅 init 方法
    eventbus.customOn("init", () => {
      init();
    });
  });

  const init = () => {
    // http
  };
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

release.vue - 发布通讯组件

html
<script lang="ts" setup>
  import { useEventbus } from "@/hooks/useEventbus";

  const eventbus = useEventbus();

  const handleClick = () => {
    // 发布通讯
    eventbus.customEmit("init");
  };
</script>
1
2
3
4
5
6
7
8
9
10
沪ICP备20006251号-1