How to create Event Bus in Vue 3?
In Vue 2, it was:
export const bus = new Vue();
bus.$on(...) bus.$emit(...)
In Vue 3, Vue
is not a constructor anymore, and Vue.createApp({});
returns an object that has no $on
and $emit
methods.
An Event Bus is nothing but a global Vue instance that is imported by the components involved in communication and passing data. It makes use of the $on, $emit, and $off properties of the Vue object to emit out events and pass on data.
Emitting Events with setup()$emit() to send our event. Instead, we can access our emit method by using the second argument of our setup function – context . context has access to your components slots, attributes, and most importantly for us, its emit method. We can call context.
An event bus is a pipeline that receives events. Rules associated with the event bus evaluate events as they arrive. Each rule checks whether an event matches the rule's criteria. You associate a rule with a specific event bus, so the rule only applies to events received by that event bus.
As suggested in official docs you could use mitt library to dispatch events between components, let suppose that we have 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(); const 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 :
Create a file src/composables/useEmitter.js
import { getCurrentInstance } from 'vue' export default function useEmitter() { const internalInstance = getCurrentInstance(); const emitter = internalInstance.appContext.config.globalProperties.emitter; return emitter; }
And from there on you can use useEmitter
just like you would with useRouter
:
import useEmitter from '@/composables/useEmitter' export default { setup() { const emitter = useEmitter() ... } ... }
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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With