© BAIVECTOR/Shutterstock.com
Advanced Vue - Teil 1: Datenfluss und Dependency Injection

Durch-, Rum- oder Reinreichen?


Was soll denn dieser Titel schon wieder? Wie sich im weiteren Verlaufe dieses Beitrags herausstellen wird, handelt es sich durchaus um eine passende Etikette, um den ersten Teil unserer Serie über fortgeschrittene Themen rund um Vue einzuleiten. In diesem Beitrag geht es um die Frage der Kommunikation und des Datenflusses zwischen den Komponenten einer Vue-Anwendung und wie Dependency Injection hier eingesetzt werden kann.

Zunächst stellt sich die Frage, was es denn eigentlich ist, was hier durch-, rum- oder reingereicht werden soll. Das sind natürlich die Daten. Bei Geschäftslogik passiert das eher selten. Daten werden in modernen auf JavaScript setzenden Single-Page-Anwendungen immer weniger als Klassen und viel häufiger in Form von Funktionen (Stichwort „funktionale Programmierung“) realisiert. Organisiert werden sie über den Standard ES Modules in einer Ordnerstruktur. Über das import-Statement schließlich kann Funktionalität ganz leicht hinzugeholt und verwendet werden.

Aus anderen Programmiersprachen wie Java oder .NET kennt man Dependency Injection (DI) als Mittel, um sich Geschäftslogik, die aus einer ganzen Reihe voneinander abhängiger Bausteinen (Klassen) besteht, initialisieren und erzeugen zu lassen. Hier kann man zum Beispiel einen Service nennen, der ein DAO (Data Access Object) verwendet und auf eine Datenbankverbindung angewiesen ist, um letztendlich mit der Datenquelle zu kommunizieren. Es werden komplexe, voneinander abhängige Bausteine initialisiert, bereitgestellt und „reingereicht“ beziehungsweise injiziert, um sie letztendlich zu benutzen. Das ist die Anforderung, bei der Dependency Injection seine vollen Stärken ausspielt.

In einer Vue-Anwendung wird man eher weniger auf diese Anforderung stoßen. Wie eingangs erwähnt, reicht in den meisten Fällen der einfache Import der gewünschten Funktionalität, und wenn in einer Funktion doch mehr Abhängigkeiten benötigt werden, sieht man in der Praxis eher Fabrikmethoden als typisches DI.

Jetzt stellt sich natürlich die berechtigte Frage: Warum dann diesem Thema einen ganzen Beitrag widmen? Hier ist festzuhalten, dass es auch in JavaScript durchaus Situationen gibt, in denen DI hilfreich ist und auch genutzt wird. Zum Beispiel im Umfeld von Unit Testing. Beim Erzeugen von Mock-Objekten und dergleichen ist der Einsatz von DI denkbar. Auch in Vue gibt es Situationen, in denen DI gewinnbringend eingesetzt werden kann. Und hier schließt sich der Kreis, man ist wieder bei den Daten der Anwendung angelangt.

Aller guten Dinge sind drei

DI ist eine Möglichkeit, auf die man zurückgreifen kann, um Daten durch eine Vue-Anwendung zu reichen. Es gibt aber mindestens vier Wege, mit denen man den Datenfluss realisieren kann:

  • Props/Emit

  • $root/$parent

  • Global Stores und State Management

  • provide/inject

Von diesen vier Ansätzen muss von Einem, nämlich $root/$parent, dringend abgeraten werden. Der Vollständigkeit halber und zum besseren Verständnis wird aber auch dieses Antipattern beschrieben. Somit gilt: Aller guten Dinge sind drei.

In einem früheren Artikel [1] wurde bereits erklärt, dass eigentlich fast alle modernen Frameworks (Angular, React, Svelte und natürlich auch Vue) komponentenorientiert arbeiten. Eine Anwendung besteht also aus mindestens einer, im Normalfall jedoch einer hierarchischen Struktur mehrerer Komponenten (Baum). Die Komponenten können in Abhängigkeit zueinander stehen und Daten austauschen. Als Beispiel für diesen Artikel dient eine Anwendung, mit der man nach Flaggen suchen und diese anzeigen kann [2]. Abbildung 1 zeigt die Struktur der Anwendung inklusive ihrer Aufteilung in Komponenten. Den zusammenhaltenden Rahmen bildet die Anwendungskomponente, die in der Datei App.vue implementiert ist. An diesem Anwendungsrahmen hängen zwei Unterkomponenten: Der Titel (AppHeader.vue) und ein Panel (CountryPanel.vue), in dem sich die Komponenten befinden, die mit dem Benutzer interagieren (CountrySearch.vue und CountryFlag.vue).

teufel_vue_1.tif_fmt1.jpgAbb. 1: Eine Vue-Anwendung besteht aus Komponenten

Es ist wichtig, zu verstehen, dass der Anwendungsrahmen (App.vue) nicht wirklich die Wurzel und damit die oberste Komponente ist. Die Wurzel bezeichnet man in Vue als $root und ist das erste Vue-Objekt, welches ganz zu Beginn der Anwendung (main.js) erzeugt wird und dafür sorgt, dass App.vue angezogen und mit dem <div> im Markup zusammengebracht wird.

Komponenten sind zunächst isoliert zu betrachten. Wenn man also einen Begriff in das Suchfeld (CountrySearch.vue) eingibt, so ist diese Eingabe vorerst nur hier sichtbar. Erst mit den weiter oben aufgelisteten Techniken versetzt man sich in die Lage, andere Komponenten zu informieren, dass hier jetzt eine Eingabe erfolgt ist.

Durchreichen

Props/Emit ist die mit Abstand am meisten genutzte Technik, um Kommunikation zwischen Komponenten zu ermöglichen. Es ist der in der Dokumentation empfohlene Standard. Bei Props/Emit werden die Daten innerhalb der Komponentenhierarchie in Form von Props von oben nach unten durchgereicht. Erfolgt in einer weiter unten eingelagerten Komponente eine Veränderung, werden die darüberliegenden Schichten über Event Handling informiert. Das Auslösen eines Events erfolgt dabei über die Funktion $emit, die von Vue zur Verfügung gestellt wird (Abb. 2). Das Konzept ähnelt React, wo State ebenfalls über Props von oben nach unten durch jede Komponente geleitet wird.

teufel_vue_2.tif_fmt1.jpgAbb. 2: Bei Props/Emit werden Daten von oben nach unten durchgereicht, von unten nach oben werden Informationen über Events weitergegeben

Der Vorteil von Props/Emit ist, dass ganz klar definiert ist, welche Daten an die untergeordneten Komponenten weitergegeben werden. Und jede Komponente erwartet ihrerseits ein Event, um über Veränderungen in den Daten oder Aktionen in darunterliegenden Komponenten informiert zu werden. Dieses Modell führt zu gut wiederverwendbaren Komponenten, da das Datenmodell explizit und nicht von äußeren Umständen abhängig ist. Komponenten, die mit Props/Emit kommunizieren, lassen sich im Übrigen auch isolieren und sind somit im Rahmen von Unit Testing gut testbar. Ein Nachteil kann sein, dass es sich bei Props/Emit um eine Lösung handelt, die nur in Vue-Projekten richtig gut funktioniert. Wenn man sich jedoch in einer Micro-Frontend-basierten Architektur bewegt, in der die verschiedenen Komponenten womöglich mit unterschiedlichen Frameworks realisiert wurden, dann kann es mit Props/Emit schwierig werden, zum Beispiel aus einer Vue-Komponente eine darüberliegende React-Komponente zu informieren.

In der diesen Artikel begleitenden Beispielanwendung [2] findet sich die Verwendung der Props/Emit-Technik im Branch props_emit. Listing 1 zeigt, wie im Anwendungsrahmen (App.vue) zunächst eine (Event-Handler-)Methode festgelegt wird, die aufgerufen werden soll, wenn das Ereignis countryChanged auftritt. Gleichzeitig ist eine Property country definiert, die zu Beginn mit dem Länderkürzel von Großbritannien vorinitialisiert wird und an die untergeordnete Komponente zur Flaggenanzeige (CountryFlag.vue) durchgereicht wird.

Listing 1: Ein Ereignis definieren und eine Property nach unten durchreichen

<template> <div id="app"> <AppHeader title="Welcome to Flag Search" /> <CountryPanel @countryChanged="countryChanged"> <CountrySearch /> <CountryFlag :country="country" /> </CountryPanel> ...

Wenn nun das countryChanged-Ereignis auftritt, wird in der zugehörigen Funktion die Property aktualisiert, und der neue Wert wande...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Gewinner-Teams

Wir bieten Lizenz-Lösungen für Teams jeder Größe: Finden Sie heraus, welche Lösung am besten zu Ihnen passt.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang