Table of Contents
URL: https://www.progressiverobot.com/vuejs-global-event-bus/
Introduction
The event bus / publish-subscribe pattern is a way of getting unrelated sections of your application to talk to each other.
The event system used in Vue components can be used in an event bus / publish-subscribe pattern.
Note: This tutorial is specific for Vue 2. In Vue 3, $on, $off, and $once have been removed. External libraries that provide this functionality are recommended.
In this article, you will apply Vue's powerful built-in event bus.
Prerequisites
To complete this tutorial, you will need:
- Node.js installed locally, which you can do by following How to Install Node.js and Create a Local Development Environment.
- Some familiarity with setting up a Vue.js project and using Vue.js components may be beneficial.
This tutorial was verified with Node v15.3.0, npm v6.14.9, and vue v2.6.11.
Step 1 — Setting Up The Project
For the purpose of this tutorial, you will build from a default Vue project generated with @vue/cli.
npx @vue/cli create <^>vue-event-bus-example<^> --default
This will configure a new Vue project with default configurations: Vue 2, babel, eslint.
Navigate to the newly created project directory:
cd <^>vue-event-bus-example<^>
You will need to create the event bus and export it somewhere so other modules and components can use it. First, create a new file. Import the Vue library. Then, export an instance of it.
[label src/event-bus.js]
import Vue from 'vue';
export const EventBus = new Vue();
For this tutorial, the instance was set to the variable <^>EventBus<^>.
What you are essentially getting is a component that is entirely decoupled from the DOM or the rest of your app. All that exists on it are its instance methods.
Now that you have created the event bus, you will need to import it into your components and call the same methods that you would use if you were passing messages between parent and child components.
Next, let's apply <^>EventBus<^>.$emit().
Step 2 — Sending Events
Consider a scenario with a component that notifies the entire app of how many times it has been clicked whenever someone clicks on it.
Note: This example uses a single-file-component here, but you can use whatever method of creating components you would like.
Here is how you would go about implementing that using <^>EventBus<^>.$emit(channel: string, payload1: any, ...):
[label src/components/ClickCountButton.vue]
<template>
<button @click="emitGlobalClickEvent()">{{ clickCount }}</button>
</template>
<script>
import { EventBus } from '@/event-bus';
export default {
data() {
return {
clickCount: 0
}
},
methods: {
emitGlobalClickEvent() {
this.clickCount++;
EventBus.$emit('clicked', this.clickCount);
}
}
}
</script>
This code produces a button. Clicking on the button would send the event on a channel (clicked) with a payload (clickCount).
Modify App.vue to use this component.
[label src/App.vue]
<template>
<div id="app">
<^><ClickCountButton></ClickCountButton><^>
</div>
</template>
<script>
<^>import ClickCountButton from './components/ClickCountButton'<^>
export default {
name: 'App',
components: {
<^>ClickCountButton<^>
}
}
</script>
Next, let's apply <^>EventBus<^>.$on.
Step 3 — Receiving Events
Now, any other part of your app can import the event bus and listen on the clicked channel using <^>EventBus<^>.$on(channel: string, callback(payload1,...)).
Apply this to your application by modifying App.vue:
[label src/App.vue]
<script>
<^>import { EventBus } from './event-bus';<^>
import ClickCountButton from './components/ClickCountButton'
export default {
name: 'App',
components: {
ClickCountButton
}
}
<^>const clickHandler = function(clickCount) {<^>
<^>console.log(`The button has been clicked ${clickCount} times!`)<^>
<^>}<^>
<^>EventBus.$on('clicked', clickHandler);<^>
</script>
This code creates an event listener for clicked and logs a message to the console with the number of times the button has been clicked.
Note: If you would only like to listen for the first emission of an event, you can use <^>EventBus<^>.$once(channel: string, callback(payload1,...)).
Next, let's apply <^>EventBus<^>.$off.
Step 4 — Removing Event Listeners
You can unregister the handler from the clicked channel using <^>EventBus<^>.$off(channel: string, callback(payload1,...)).
Apply this to your application by modifying App.vue:
[label src/App.vue]
<script>
import { EventBus } from './event-bus';
import ClickCountButton from './components/ClickCountButton'
export default {
name: 'App',
components: {
ClickCountButton
}
}
const clickHandler = function(clickCount) {
console.log(`The button has been clicked ${clickCount} times!`)
}
EventBus.$on('clicked', clickHandler);
<^>EventBus.$off('clicked', clickHandler);<^>
</script>
By providing an event and a callback, <^>EventBus<^>.$off will only remove the listener for this specific callback.
Note: You could also remove all listeners for a particular event using <^>EventBus<^>.$off('clicked') with no callback argument.
And if you really need to remove every single listener from <^>EventBus<^>, regardless of channel, you can call <^>EventBus<^>.$off() with no arguments at all.
Now, you have utilized .$emit, .$on, and .$off.
Conclusion
In this tutorial, you used Vue's powerful built-in event bus to listen for a clicked event and log a message with the click count. This was achieved by utilizing .$emit, .$on, and .$off.
If you'd like to learn more about Vue.js, check out our Vue.js topic page for exercises and programming projects.