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.
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, ...)
:
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.
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
:
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
:
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.