Instead of relying on the traditional event bus in Vue, you can utilize the mitt library to dispatch events between components. Let's say we have a sidebar and a header that contains a button to open/close the sidebar, and we want that button to toggle a property inside the sidebar component:
In your main.js file, import the mitt library and create an emitter instance, defining it 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 the header component, 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 the sidebar component, 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>
If you are using the composition API, you can create a composable event bus like so:
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;
}
Then, you can use the useEmitter function in your components just like you would with useRouter:
import useEmitter from '@/composables/useEmitter'
export default {
setup() {
const emitter = useEmitter()
...
}
...
}
Using the composition API
You can take advantage of the new composition API to define a composable event bus:
eventBus.js
import { ref } from "vue";
const bus = ref(new Map());
export default function useEventsBus(){
function emit(event, ...args) {
bus.value.set(event, args);
}
return {
emit,
bus
}
}
In Component A:
import useEventsBus from './eventBus';
...
//in script setup or inside the setup hook
const {emit}=useEventsBus()
...
emit('sidebarCollapsed',val)
In Component B:
const { bus } = useEventsBus()
watch(()=>bus.value.get('sidebarCollapsed'), (val) => {
// destructure the parameters
const [sidebarCollapsedBus] = val ?? []
sidebarCollapsed.value = sidebarCollapsedBus
})