EventBus from Vue2.x to Vue3
Preface
In Vue2, when you want to do cross-component communication, besides Vuex, the event bus pattern is very common in smaller projects. But Vue3 removed $on, $off, $emit and similar APIs, so the event bus can no longer rely on those APIs to listen. However, in Vue3 you can use the third-party library mitt.
Event Bus in Vue2.x
Method 1: Add an event bus property in main.js
In main.js:
//Add a property on the Vue instance
Vue.prototype.$eventBus = new Vue();
In the component that listens for events:
this.$on("event name",(*args)=>{
...
})
In the component that emits events:
this.$emit("event name",*args)
Method 2: Create a new Vue instance directly and use it as the EventBus
In eventBus.js:
import Vue from "vue";
export const EventBus = new Vue();
In the component that listens for events:
import { EventBus } from "@/eventBus";
EventBus.$on("event name",(...args)=>{
...
})
In the component that emits events:
import { EventBus } from "@/eventBus";
EventBus.$emit("event name",...args)
Method 3: Refactor eventBus into a plugin
This is the approach used in a previous company project, because we needed to use the EventBus on both Vue and on window at the same time.
In EventBus.js:
import Vue from 'vue';
class EventBus {
constructor() {
this.bus = new Vue();
}
on(event, handler) {
this.bus.$on(event, handler);
}
once(event, handler) {
this.bus.$once(event, handler);
}
off(event, handler) {
this.bus.$off(event, handler);
}
emit(event, ...args) {
this.bus.$emit(event, ...args);
}
}
export default {
install(Vue) {
const eventBus = new EventBus();
Vue.prototype.$EventBus = eventBus;
window.EventBus = eventBus;
}
};
Then register it in main.js:
import Vue from 'vue';
import EventBus from '@/plugins/EventBus';
Vue.use(EventBus);
After that you can listen for and emit events the same way as above.
Event Bus in Vue3
Since Vue3 removed $on, $off, $emit, etc., we use the third-party library mitt instead.
Create an entry file for the eventBus (entry.ts):
import mitt, { Emitter } from 'mitt';
const EventBus: Emitter<any> = mitt();
export default EventBus;
In my current project I treat the event bus as a hook, so I write it like this:
(useDataHook.ts)
import EventBus from '@/hooks/eventBus/entry';
import { onMounted } from 'vue';
export default function () {
onMounted(() => {
EventBus.on("event name",(...args)=>{
...
})
});
}
(DemoListener.vue)
import useDataHook from '@/hooks/eventBus/useDataHook';
setup(){
useDataHook()
}
(DemoTrigger.vue)
import EventBus from '@/hooks/eventBus/entry';
setup(){
function demoTrigger(){
EventBus.emit("event name", ...args)
}
return {demoTrigger}
}
ChangeLog
- 20260501–translate by claude code