Search code examples
vue.jsevent-handlingvue-routerevent-bus

How do I create a Vue bus for global event handling?


I'm using Vue on a Node/Webpack/Vue Router environment and trying to setup a global event handler or bus. But it's not working. It's showing up as undefined. Here's my setup:

main.js:

//Declare event handler
Object.defineProperty(Vue.prototype, '$bus', {
  get () {
    return this.$root.bus
  }
})

//Declare app instance
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

App.vue

<script>
export default {
  name: 'App'
  created: () => {
    console.log(this.$bus);
  }
}
</script>

The console.log statement returns undefined, meaning the event handler somehow isn't getting passed to the app. I've also tried the following statement to declare the event handler:

main.js

//Declare event handler
const eventBus = new Vue()
Vue.prototype.$bus = eventBus

But that doesn't work either.


Solution

  • This is a problem with using the arrow function. While the () => {} syntax looks nicer than function() {}, there is a difference, in that the the arrow function uses a lexical context for this (from where it was defined, instead of from where it was called, which id what you need in this instance), meaning that this is no longer the Vue instance, so you cannot use this.$bus. You can fix this by replacing the arrow function with a regular function using either created: function() {... or the concise (but functionally equivalent) version created() {...

    You can read up more on the differences by looking up articles on es6, arrow functions, lexical scope, this context.

    main.js code:

    import Vue from "vue";
    import App from "./App";
    
    Vue.prototype.$bus = new Vue();
    
    new Vue({
      el: "#app",
      components: { App },
      template: "<App/>"
    });
    

    somewhere in app.js

    export default {
      name: "App",
      created() {
        console.log(this.$bus);
      }
    };