Reminiscent of removing native API fetching in Vue 2 in favor of encouraging the use of third party libraries (most recommended being Axios), Vue 3 also has a breaking change: event bussing. Well, technically, Vue just wants you to only emit from parent⟵child (and parent⟶child bussing would be through the use of props). They’ve never officially encouraged developers to create an event bus outside of the main Vue instance because some consider it anti-pattern to the Vue methodology; outside of the parent⟷child emit/prop pattern, they encourage developers to use VueX when that pattern is too limiting. The downside is VueX can be overkill for a lot of smaller projects, so you’ll see a ton of event bus articles and tutorials using this publish-subscribe or “pubsub” pattern through a Google search.

In Vue 2

Previously, you’d create an event bus in the base of your project through a separate Vue instance, which you’d then use throughout your application to bus events/data between parent and child using their Events API:

/* main.js */
import Vue from 'vue';
export const bus = new Vue();

... init main Vue application ...

Emit the event in Component A

/* ComponentA.vue */
<template>
  <div>
    <button @click="bus.$emit('say-hello', 'Hey, Stranger!')">Emit Event!</button>
  </div>
</template>

<script>
import { bus } from '../main'
export default {
  name: 'ComponentA',

...

Listen for the event in Component B

/* ComponentB.vue */
<script>
import { bus } from '../main';
export default {
  name: 'ComponentB',
  created (){
    bus.$on('say-hello', (greeting) => alert(greeting))
  }
}
</script>

In Vue 3 this is no longer possible because while $emit still works for child->parent communication, $on, $off, and $once are no longer available to the instance (more about that here).

So, what do we do in Vue 3?

Luckily it’s really simple to add an external library to handle it for us in much the same way. In this example, I’ll use Mitt.
I’ll use emitter instead of bus which follows their example code and hopefully makes it easier to spot the difference having used bus in the previous example.

/* main.js */
import App from '@/App.vue'
import mitt from 'mitt'
import { createApp } from 'vue'

const emitter = mitt()
const app = createApp(App)

app.config.globalProperties.emitter = emitter
app.mount('#app')

Emit the event in Component A

/* ComponentA.vue */
<template>
  <div>
    <button @click="emitter.emit('say-hello', 'Hey, Stranger!')">Emit Event!</button>
  </div>
</template>

<script>
export default {
  name: 'ComponentA',

...

Listen for the event in Component B

/* ComponentB.vue */
<script>
import { bus } from '../main';
export default {
  name: 'ComponentB',
  created (){
    this.emitter.on('say-hello', (greeting) => alert(greeting))
  }
}
</script>

In Conclusion

Ultimately, like any breaking change, it can be a hassle to update projects but in the end it ends up being pretty simple and the code looks largely the same. While certainly Vue’s developers have their opinions on how they’d like to see Vue used, they’ve always done a great job of offering up compromises where their framework is still flexible when you add features or patterns outside of their normal workflow. The developer wins, too, because libraries like Mitt can focus on supporting features that are outside of Vue’s feature scope.

#vue #emit #publish-subscribe #pubsub #bus #event

Event Bus (PubSub) in Vue 3
128.90 GEEK