首页 > 编程笔记 > JavaScript笔记 阅读:18

Vue mitt库实现组件通信(附带实例)

在 Vue 中,利用第三方类库 mitt 可以实现非父子之间的通信。

执行下方命令,在当前项目中安装依赖:
npm install mitt -save
安装依赖后,需要在当前项目中引入并创建进行事件处理的 emitter 对象。在 src 目录下创建 services 目录,并在其中创建 emitter.js 文件,在该文件中只需要引入 mitt 类库,得到其暴露的 mitt() 函数,执行 mitt() 函数产生 emitter 对象,并对其进行默认暴露。

src/services/emitter.js 文件代码如下:
import mitt from 'mitt';
export default mitt();
emitter 对象主要提供了 on()、emit()、off()、all() 这 4 个方法,可以利用 on() 方法监听自定义事件,利用 emit() 方法分发自定义事件,利用 off() 方法取消特定自定义事件,以及利用 all() 方法取消所有事件。

假如在父组件 App 中有两个子组件 Child1 和 Child2,我们不想通过父组件 App 来实现两个子组件之间的通信,而是希望直接使两个子组件之间产生通信。这其实就是非父子之间通信的一种方式,按照这两个子组件的关系,其属于兄弟组件。

按照上面需求的描述,App 组件只是对两个子组件进行引入,不存在属性传递和事件监听,那么 App.vue 文件代码就很简单了,具体如下:
<script setup>
import Child1 from './components/Child1.vue';
import Child2 from './components/Child2.vue';
</script>

<template>
  <Child1 />
  <Child2 />
</template>
假如我们要实现 Child2 向 Child1 发送一个数值,Child1 收到这个数值后,累加显示到原有的 count 数值上。

在 Child2 中我们就可以利用 emitter 对象的 emit() 方法来分发事件,并指定一个特定的要增加的数量。

components/Child2.vue 文件代码如下:
<template>
  <div>
    <h1>Child2</h1>
    <button @click="increase">increase</button>
  </div>
</template>

<script setup>
import emitter from '../services/emitter'; // 引入事件总线

// 在组件内部分发事件
const increase = () => {
  emitter.emit('increaseCount', 2);
};
</script>
在按钮的点击回调中,通过 emitter 对象的 emit() 方法分发了自定义事件 increaseCount,并指定了要传递的数量为 2。

而 Child1 要利用 emitter 对象绑定自定义事件,指定接收数据的回调函数,在回调函数中更新要显示的 count 数值,并且需要在合适的时机取消绑定的事件。

components/Child1.vue 文件代码如下:
<template>
  <div>
    <h1>Child1</h1>
    <p>count:{{ count }}</p>
  </div>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue';
import emitter from '../services/emitter'; // 引入事件总线
const count = ref(0);
// 绑定事件的回调函数,接收事件参数并进行使用
const increaseCountCallback = (num) => {
  count.value += num;
};

// 在组件实例挂载完成时,订阅事件
onMounted(() => {
  emitter.on('increaseCount', increaseCountCallback);
});
// 在组件实例完全销毁前,取消订阅事件
onBeforeUnmount(() => {
  emitter.off('increaseCount', increaseCountCallback);
});
</script>
上面的代码在 onMounted 挂载完成的生命周期钩子函数中绑定了自定义事件,事件名为子组件 Child2 中的 increaseCount,事件的回调函数接收的参数 num 是分发事件时传递的数据,在回调函数中将其累加到 count 中。

值得一提的是,我们通常会在组件实例完全销毁前(onBeforeUnmount 生命周期钩子函数),利用 emitter 对象的 off() 方法对绑定的事件进行取消。

此时可以利用 Child2 中的按钮来修改 Child1 中的 count 值,轻松实现非父子组件之间的通信。但这种通信方式在组件数量越来越多、需求越来越多的情况下,代码会被分散在不同的组件中,组件的通信关系会变得越来越混乱和繁杂,甚至形成蜘蛛网结构,管理起来并不方便。

对于这种情况,我们可以考虑通过 Vuex 和 Pinia 状态管理器解决。

相关文章