Tutorial

How To Create a Global Event Bus in Vue 2

Vue.js

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:

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.

Creative Commons License