Communicatie tussen Vue componenten

Als je een Vue frontend applicatie bouwt heb je al snel te maken met meerdere componenten. Veel componenten kunnen onafhankelijk van elkaar werken en zijn daardoor makkelijk in te zetten. Soms kun je een situatie komen waarin componenten afhankelijk van elkaar zijn. Ze kunnen bijvoorbeeld dezelfde data gebruiken of ze moeten op elkaar reageren in bepaalde situaties.

Hoewel de documentatie van Vue compleet is staat er er toch weinig omschreven over de communicatie tussen componenten. Toch heb je meerdere opties. Aan de hand van vier voorbeelden kun je zien welke keuze je hebt.

Props en Events

Een veelgebruikte pattern voor de communicatie tussen componenten maakt gebruik van properties en events. Properties worden door parent-componenten gebruikt om data door te geven aan child-componenten. Child-componenten kan events triggeren, waar parents naar kunnen luisteren en indien nodig op kunnen acteren.

De communicatie van componenten middels props en events is alleen mogelijk als de relatie van de componenten een child-parent relatie is. Handig van props en events is dat dit volledig in de html van het component ingesteld kan worden. Met de :prop notatie kun je properties binden, met de @Directive kun je luisteren naar custom-events en methods aanroepen. Er is dus nauwelijks tot geen programmeerwerk nodig.

<parent>
     <child :customProp=”parentProp” @onCustomEvent=”customEventListener”></child>
</parent>

.sync modifier

Met de .sync modifier hebben we de voordelen van two-way binding zonder onderhoudsproblemen. Bij two-way binding geldt dat zowel een parent als child-component een dezelfde variable kunnen veranderen. Zodra één van de twee componenten de waarde van de variable veranderd kan de ander daar direct op acteren. Voor de lifecycle is dit een zwaar proces omdat bij elke kleine verandering een kettingreactie ontstaat. Voor dit probleem is de .sync modifier geïntroduceerd.

Waar je bij props en events een property vult en wacht op een event van een child kun je bij de sync modifier deze acties bundelen in een shorthand.

<child
  v-bind:customProp="parentProp"
  v-on:update:customProp="parentProp = $event"
></child>

hierboven zie je een voorbeeld van een prop-event relatie tussen een child en parent component. Voor elke property waar je two-way binding zou willen zou dit betekenen dat je twee attributen in de DOM zou plaatsen. De .sync modifier voor deze situatie zou als volgt zijn:

<child v-bind:customProp.sync="parentProp"></child>

EventBus

Een bekend messaging pattern is de publish-subscribe pattern, waarbij componenten die een bericht / data wilt verzenden dit niet aflevert bij specifieke andere componenten maar `publisht`. Andere componenten kunnen luisteren naar zo’n publish, wat ‘subscriben’ is. Het kenmerkende van dit pattern is dat de zender en luisteraar niets van elkaar (hoeven) weten.

Een nadeel van Pub/sub is dat het snel onduidelijk wordt welk component of welk event reageert. Het is daarom van belang dat je goed nadenkt over naamgeving en de events goed documenteert.

In Vue kun je pub/sub toepassen door events te triggeren op een nieuwe Vue instantie.

// global EventBus
const EventBus = new Vue();
// first component
export default {
  data() {
    return {
      foo: 'bar'
    }
  },
  methods: {
    emitCustomGlobalEvent() {
      EventBus.$emit('custom-event', this.foo);
    }
  } 
}
// second component
EventBus.$on('custom-event', foo => {
  console.log(foo); // should be `bar`
});

Direct aanspreken

Wat vaak gebeurt is dat componenten elkaar direct aanspreken. Een parent component weet welke child-componenten hij bevat en kan middels `this.$children` de methods en properties van het child-component aanspreken. En een child weet welke parent hij heeft en kan met `this.$parent` methods en properties van de parent opvragen.

Dit is echter niet de manier waarop je communicatie tussen componenten wilt maken. Componenten horen onafhankelijk van elkaar te werken. Als je je in een situatie bevindt dat je dit wilt introduceren is het verstandig om kritisch naar je component te kijken. Vaak kun je beter de twee componenten herschrijven naar één component of props en events gebruiken.

Conclusie

Mijn persoonlijke voorkeur gaat uit naar het gebruik van props en events. Je hoeft nauwelijks extra code te schrijven en de code blijft goed leesbaar. Het is goed zichtbaar welke componenten met elkaar communiceren. Een nadeel van props en events is dat dit alleen toe te passen is bij child-parent componenten. Daarom zul je soms niet om een EventBus heen kunnen.