Communication between sibling components in Vue.js 2.0 Communication between sibling components in Vue.js 2.0 vue.js vue.js

Communication between sibling components in Vue.js 2.0


You can even make it shorter and use the root Vue instance as the global Event Hub:

Component 1:

this.$root.$emit('eventing', data);

Component 2:

mounted() {    this.$root.$on('eventing', data => {        console.log(data);    });}


With Vue.js 2.0, I'm using the eventHub mechanism as demonstrated in the documentation.

  1. Define centralized event hub.

     const eventHub = new Vue() // Single event hub // Distribute to components using global mixin Vue.mixin({     data: function () {         return {             eventHub: eventHub         }     } })
  2. Now in your component you can emit events with

     this.eventHub.$emit('update', data)
  3. And to listen you do

     this.eventHub.$on('update', data => { // do your thing })

Update

Please see the answer by alex, which describes a simpler solution.


State scopes

When designing a Vue application (or in fact, any component based application), there are different types of data that depend on which concerns we're dealing with and each has its own preferred communication channels.

  • Global state: may include the logged in user, the current theme, etc.

  • Local state: form attributes, disabled button state, etc.

Note that part of the global state might end up in the local state at some point, and it could be passed down to child components as any other local state would, either in full or diluted to match the use-case.


Communication channels

A channel is a loose term I'll be using to refer to concrete implementations to exchange data around a Vue app.

Each implementation addresses a specific communication channel, which includes:

  • Global state
  • Parent-child
  • Child-parent
  • Siblings

Different concerns relate to different communication channels.

Props: Direct Parent-Child

The simplest communication channel in Vue for one-way data binding.

Events: Direct Child-Parent

$emit and $on. The simplest communication channel for direct Child-Parent communication. Events enable 2-way data binding.

Provide/Inject: Global or distant local state

Added in Vue 2.2+, and really similar to React's context API, this could be used as a viable replacement to an event bus.

At any point within the components tree could a component provide some data, which any child down the line could access through the inject component's property.

app.component('todo-list', {  // ...  provide() {    return {      todoLength: Vue.computed(() => this.todos.length)    }  }})app.component('todo-list-statistics', {  inject: ['todoLength'],  created() {    console.log(`Injected property: ${this.todoLength.value}`) // > Injected property: 5  }})

This could be used to provide global state at the root of the app, or localized state within a subset of the tree.

Centralized store (Global state)

Vuex is a state management pattern + library for Vue.js applications.It serves as a centralized store for all the components in anapplication, with rules ensuring that the state can only be mutated ina predictable fashion.

And now you ask:

[S]hould I create vuex store for each minor communication?

It really shines when dealing with global state, which includes but is not limited to:

  • data received from a backend,
  • global UI state like a theme,
  • any data persistence layer, e.g. saving to a backend or interfacing with local storage,
  • toast messages or notifications,
  • etc.

So your components can really focus on the things they're meant to be, managing user interfaces, while the global store can manage/use general business logic and offer a clear API through getters and actions.

It doesn't mean that you can't use it for component logic, but I would personally scope that logic to a namespaced Vuex module with only the necessary global UI state.

To avoid dealing with a big mess of everything in a global state, see the Application structure recommandations.

Refs and methods: Edge cases

Despite the existence of props and events, sometimes you might stillneed to directly access a child component in JavaScript.

It is only meant as an escape hatch for direct child manipulation -you should avoid accessing $refs from within templates or computed properties.

If you find yourself using refs and child methods quite often, it's probably time to lift the state up or consider the other ways described here or in the other answers.

$parent: Edge cases

Similar to $root, the $parent property can be used to access theparent instance from a child. This can be tempting to reach for as alazy alternative to passing data with a prop.

In most cases, reaching into the parent makes your application moredifficult to debug and understand, especially if you mutate data inthe parent. When looking at that component later, it will be verydifficult to figure out where that mutation came from.

You could in fact navigate the whole tree structure using $parent, $ref or $root, but it would be akin to having everything global and likely become unmaintainable spaghetti.

Event bus: Global/distant local state

See @AlexMA's answer for up-to-date information about the event bus pattern.

This was the pattern in the past to pass props all over the place from far up down to deeply nested children components, with almost no other components needing these in between. Use sparingly for carefully selected data.

Be careful: Subsequent creation of components that are binding themselves to the event bus will be bound more than once--leading to multiple handlers triggered and leaks. I personally never felt the need for an event bus in all the single page apps I've designed in the past.

The following demonstrates how a simple mistake leads to a leak where the Item component still triggers even if removed from the DOM.