How to create Event Bus in Vue 3?
In Vue 2, it was:
export const bus = new Vue();
On version 3 of Vue.js, you can use either a third-party library, or use the functionality written in the publisher-subscriber(PubSub concept) programming pattern.
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;
In a Vuejs project you can create a new file on the same root as App.vue and instanciate the Vue constructor.
eventBus.js file
import Vue from 'vue';
export default new Vue();
Then, you only have to import the event on every vue file you want to emit or receiving the event, like this:
import EventBus from "../eventBus.js";
To just emit and receive the event use the $emit/$on mark
EventBus.$emit("evMsg", someData);
EventBus.$on("evMsg", (someData) => {});
As suggested in official docs you could use mitt library to dispatch events between components, let suppose that we a sidebar and header which contains a button that close/open the sidebar and we need that button to toggle some property inside the sidebar component :
in main.js import that library and create an instance of that emitter and define as a global property:
Installation :
npm install --save mitt
Usage :
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')
in header emit the toggle-sidebar
event with some payload :
<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>
In sidebar receive the event with the payload:
<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>
For those using composition api they could use emitter
as follows :
import { getCurrentInstance } from 'vue'
export default{
setup(){
const internalInstance = getCurrentInstance();
const emitter = internalInstance.appContext.config.globalProperties.emitter;
...
},
...
}