问题描述
如何在Vue 3中创建事件总线?
在Vue 2中,它是:
export const bus = new Vue();
bus.$on(...)
bus.$emit(...)
在Vue 3中,Vue
不再是构造函数,并且Vue.createApp({});
返回的对象没有$on
和$emit
方法。
解决方法
按照官方docs中的建议,您可以使用mitt库在组件之间分派事件,假设我们有一个侧边栏和标题,其中包含一个用于关闭/打开侧边栏的按钮,我们需要该按钮切换侧边栏组件内的某些属性:
在main.js中导入该库并创建该发射器的实例并定义为global property:
安装:
npm install --save mitt
用法:
import { createApp } from 'vue'
import App from './App.vue'
import mitt from 'mitt';
const emitter = mitt();
let app=createApp(App)
app.config.globalProperties.emitter = emitter
app.mount('#app')
标题中的toggle-sidebar
事件带有一些有效载荷:
<template>
<header>
<button @click="toggleSidebar"/>toggle</button>
</header>
</template>
<script >
export default {
data() {
return {
sidebarOpen: true
};
},methods: {
toggleSidebar() {
this.sidebarOpen = !this.sidebarOpen;
this.emitter.emit("toggle-sidebar",this.sidebarOpen);
}
}
};
</script>
在边栏中接收带有有效负载的事件:
<template>
<aside class="sidebar" :class="{'sidebar--toggled':!isOpen}">
....
</aside>
</template>
<script>
export default {
name: "sidebar",data() {
return {
isOpen: true
};
},mounted() {
this.emitter.on("toggle-sidebar",isOpen => {
this.isOpen = isOpen;
});
}
};
</script>
对于使用composition api的用户,他们可以如下使用emitter
:
import { getCurrentInstance } from 'vue'
export default{
setup(){
const internalInstance = getCurrentInstance();
const emitter = internalInstance.appContext.config.globalProperties.emitter;
...
},...
}
,
在Vue.js的第3版上,您可以使用第三方库,也可以使用以Publisher-Subscriber(PubSub概念)编程模式编写的功能。
event.js
//events - a super-basic Javascript (publish subscribe) pattern
class Event{
constructor(){
this.events = {};
}
on(eventName,fn) {
this.events[eventName] = this.events[eventName] || [];
this.events[eventName].push(fn);
}
off(eventName,fn) {
if (this.events[eventName]) {
for (var i = 0; i < this.events[eventName].length; i++) {
if (this.events[eventName][i] === fn) {
this.events[eventName].splice(i,1);
break;
}
};
}
}
trigger(eventName,data) {
if (this.events[eventName]) {
this.events[eventName].forEach(function(fn) {
fn(data);
});
}
}
}
export default new Event();
index.js
import Vue from 'vue';
import $bus from '.../event.js';
const app = Vue.createApp({})
app.config.globalProperties.$bus = $bus;
,
借助 Vue 组合和 defineEmit,您甚至可以更轻松:
<!-- Parent -->
<script setup>
import { defineEmit } from 'vue'
const emit = defineEmit(['selected'])
const onEmit = (data) => console.log(data)
</script>
<template>
<btnList
v-for="x in y"
:key="x"
:emit="emit"
@selected="onEmit"
/>
</template>
<!-- Children (BtnList.vue) -->
<script setup>
import { defineProps } from 'vue'
const props = defineProps({
emit: Function
})
</script>
<template>
<button v-for="x in 10" :key="x" @click="props.emit('selected',x)">Click {{ x }}</button>
</template>
我只是给一个孩子展示了它,但是你可以通过 emit 函数传递给其他孩子。
,在Vuejs项目中,您可以在与App.vue相同的根目录下创建一个新文件,并实例化Vue构造函数。
eventBus.js文件
import Vue from 'vue';
export default new Vue();
然后,您只需要在要发送或接收事件的每个vue文件上导入事件,如下所示:
import EventBus from "../eventBus.js";
要发出和接收事件,请使用$ emit / $ on标记
EventBus.$emit("evMsg",someData);
EventBus.$on("evMsg",(someData) => {});