Vue.js 3 Event Bus Vue.js 3 Event Bus vue.js vue.js

Vue.js 3 Event Bus


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();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) patternclass 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;


With Vue composition and defineEmit you can even make it easier :

<!-- 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>

I just showed it with one children, but you could pass though the emit function down to other children.