Mit Vue.js programmieren Sie Webanwendungen mit nur wenigen Zeilen Code innerhalb einer einzigen HTML-Seite.
Der Durchmarsch der Javascript-Bibliothek JQuery [1] ab 2005 zeigte, dass nur wenige Entwickler direkt mit der W3C-spezifizierten Schnittstelle zwischen HTML und Javascript arbeiten wollten. Seitdem ist die Zeit nicht stehen geblieben: Neuere Bibliotheken wie Vue.js [2] kapseln mittlerweile nicht mehr nur DOM und Event Handler, sondern knüpfen quasi magische Verbindungen zwischen HTML (View) und Datenmodell, was Ihnen viel Arbeit abnimmt.
Webanwendungen, die nach jeder Interaktion mit dem Benutzer die Seite im Browser neu laden, erreichen niemals den Komfort einer Desktop-Anwendung: Viel zu lange dauert das Übertragen der Daten und der Neuaufbau der Seite. Moderne Anwendungen arbeiten daher nach dem Single-Page-Prinzip: Eine Seite bleibt durchgängig offen und verändert sich dynamisch.
Motor dieser Dynamik ist ein Javascript-Programm. Beim Entwickeln dieser oft komplexen Programme knirscht es am häufigsten an der Schnittstelle zwischen Javascript und HTML-Seite: Event-Handler und DOM-Interface bleiben meilenweit hinter Desktop-Lösungen wie dem Signal-Slot-Mechanismus von Qt [3] zurück. Daher setzen Developer in der Regel auf kapselnde Bibliotheken wie Vue.js, statt direkt mit den Browser-APIs zu arbeiten.
Bindemittel
Das Framework verkittet Oberfläche (HMTL-Seite) und Javascript-Code mit knappen, gut lesbaren Direktiven, die die umständlichen DOM-Zugriffe per getElementById() und das Setzen von Event Handlern mit addEventListener() überflüssig machen. Genauer gesagt verbindet die Software Datenmodell, Eingabefelder und Bedienelemente wie Buttons nach dem Observer-Design-Muster.
Höchste Zeit für ein konkretes Beispiel: In einer Shop-Anwendung gibt es für jeden Posten ein Eingabefeld Anzahl. Am Ende der Liste stehen die Gesamtkosten (Abbildung 1).

Abbildung 1: Bereits diese simple statische Liste demonstriert das Prinzip von Vue.js: Ohne expliziten Javascript-Code verschwinden die Einträge, wenn die Stückzahl in der mittleren Spalte auf null fällt.
In klassischer Event-basierter Javascript-Programmierung wäre es nötig, dass Sie an jedes Feld Stückzahl einen Event-Handler knüpfen, der es auf Änderungen überwacht, gegebenenfalls die neue Summe berechnet und außerdem einen Posten aus der Liste entfernt, falls seine Stückzahl auf null fällt. Mit Vue.js erreichen Sie die gleiche Funktion, aber auf anderem Weg. Listing 1 zeigt eine erste, noch sehr rudimentäre Ausbaustufe.
Listing 1
<script src="https://unpkg.com/vue"></script>
<div id="basket">
<ul> <!-- Liste aus Schleife über "items[]"-->
<li v-for="item in items" v-if="item.number > 0">
{{ item.text }},
<!--input mit Vue.js-Datenbindung-->
<input v-model="item.number"> Mal,
Preis: {{ item.price }}
</li>
</ul>
</div>
<script>
//Vue-Konstruktor
var basket = new Vue({
el: '#basket',
data: {
//drei feste vorgegebene Beispiele für den Warenkorb
items: [
{text: 'erster Artikel', price: 22.5, number: 1},
{text: 'zweiter Artikel', price: 42, number: 3},
{text: 'dritter Artikel', price: 12.8, number: 2}
]
}
})
</script>
Die erste Zeile von Listing 1 bindet die aktuelle Version der Bibliothek von einem Content-Delivery-Network ein. Das ermöglicht es, die Listings aus dem Artikel direkt im Browser zu öffnen, ohne vorher etwas herunterzuladen. Natürlich funktioniert der Code ebenfalls, wenn Sie Vue.js als lokale Datei oder über einen Webserver einbinden [4].
Im ersten Beispiel listet das Programm lediglich drei vorgegebene Artikel als ungeordnete Liste (<ul> ). Dennoch sind schon ein paar grundlegende Bestandteile einer solchen Anwendung zu sehen: Im HTML-Code stehen spezifische Attribute mit dem Präfix v-, nämlich v-for v-if und v-model (Zeile 5 und Zeile 8). Der Browser ignoriert diese, aber die Bibliothek findet sie im HTML-Code. Doppelt geschweifte Klammern binden nach Art des bekannten Template-Systems Moustache [5] Variablen in die Seite ein.
Der Javascript-Block innerhalb des Tags script legt mit new ein neues Vue-Objekt mit einigen Attributen an (Zeile 16): Das obligatorische Attribut el bindet die Anwendung in den DOM-Baum der Seite ein.
Das Objekt data (Zeile 18) übergibt beim Start einen Datenbestand, der hier nur aus dem Array items mit Objekten aus den drei Feldern text, price und number besteht. Eine fertige Anwendung bezieht diese Einträge aber nicht mehr aus dem Programmcode, sondern per Ajax-Aufruf vom Server.
Die Direktive v-for (Zeile 5) funktioniert wie eine Foreach-Schleife: Das Programm wiederholt den zugehörigen Tag samt Inhalt für jeden Eintrag des Arrays. Die im Array item hinterlegten Einträge setzen Sie im HTML-Code per doppelter Klammer ein, innerhalb der Schleife steht gemäß dem Attribut item in items die aktuelle Zeile in der Variablen item bereit. Die Doppelklammer-Ausdrücke {{ item.text }} und {{ item.price }} (Zeile 6 und 9) geben die Felder text und price wider.
Implizite Logik
Bisher hat Vue.js nicht mehr geleistet, als ein gewöhnliches Template-System. Interessanter sind die Direktiven v-if und v-data: Geben Sie in ein Eingabefeld für die Anzahl den Wert 0 ein, dann verschwindet die zugehörige Zeile automatisch aus der Liste (Abbildung 2).
![Abbildung 2: Eine Eingabe von <code>0</code> in der mittleren Spalte führt dazu, dass die Software die zweite Zeile ausblendet. Zusätzlich hat eine Modifikation von <code>basket.items[0].text</code> den Namen des Artikels in Zeile 1 verändert.](/wp-content/uploads/2018/12/b02-konsole-300x161.jpg)
Abbildung 2: Eine Eingabe von 0 in der mittleren Spalte führt dazu, dass die Software die zweite Zeile ausblendet. Zusätzlich hat eine Modifikation von basket.items[0].text den Namen des Artikels in Zeile 1 verändert.
Folgendes passiert dabei: Mit v-model="item.number" (Zeile 8) binden Sie das Eingabefeld in der Mitte der Listenzeilen an das Feld number der Item-Objekte. Jede Eingabe eines Benutzers verändert daher direkt den entsprechenden Eintrag des Arrays. Dazu sind weder Event-Handler noch anderer Javascript-Code nötig.
Die HTML-Liste brauchen Sie ebenfalls nicht explizit neu zu generieren: Vue.js hält sie automatisch mit dem Array items synchron. Dies testen Sie bei Bedarf in der Javascript-Konsole des Browsers: Unter Firefox öffnen Sie den Entwickler-Bereich mit [F12] und wechseln zum Reiter Konsole. Geben Sie im Eingabefeld ganz unten den Code basket.items[0].text="geänderter Artikel" ein, dann ändert sich der Name des Artikel in der ersten Zeile.
Schon dieses einfache Beispiel verdeutlicht das grundlegende Prinzip von Vue.js: Knappe Direktiven mit dem Präfix v- knüpfen wechselseitige Verbindungen zwischen dem Datenmodell und der angezeigten Seite. Diese Deklarationen ersetzen ausgeschriebenen Programmcode, der Eingabefelder und Datenmodell auf Veränderungen überwacht und gegebenenfalls für ein Update des HTML-Codes sorgt. Diese Architektur hat Microsoft 2005 unter dem Namen Model-View-ViewModel (MVVM) (Abbildung 3) als Variante des bekannten Patterns Model-View-Controller (MVC) vorgestellt.

Abbildung 3: Vue.js orientiert sich an der MVVM-Architektur: Direktiven verknüpfen Model und View, manchmal schalten sich noch Computed Properties oder Watches dazwischen.
Ziel war es, den Entwurf von Oberflächen durch das enge Verknüpfen von Model und View zu beschleunigen: Eine bei Veränderungen an der GUI anzupassende Controller-Zwischenschicht fällt dabei weg. Dafür ist das ViewModel bloß als temporärer Auszug eines persistenten Datenbestands auf dem Server oder im sogenannten Local Storage des Browsers gedacht.
MMVM bedeutet aber nicht, dass Anwendungen nur unverarbeitete Daten aus dem Datenmodell anzeigen: Für das vorhergehende Verarbeiten sorgen in Vue.js die sogenannten Computed Properties [6]. Binden Sie Elemente einer Seite daran, so zeigt die Seite genau wie bei einer direkten Bindung an ein Datenfeld stets die aus den aktuellen Daten errechneten Werte.
Listing 2 demonstriert, wie Sie eine solche Computed Property definieren: Fügen Sie dem an den Konstruktor new Vue() übergebenen Objekt das Unterobjekt computed hinzu (Zeile 63). Im Beispiel enthält es lediglich die simple Funktion sum, die aus den Feldern price und number (Stückzahl) in items die Gesamtkosten errechnet.
Listing 2
<script src="https://unpkg.com/vue"></script>
<style>
.inactive{color: #cccccc;}
input.number{width: 3em}
caption {font-weight: bold}
table{border-spacing: 1px; background-color: #cccccc}
td{background-color: white; padding: 5px;}
.bold{font-weight: bold}
</style>
<div id="basket">
<table>
<caption>Einkaufskorb</caption>
<tr v-if="items.length == 0">
<td colspan="4"> Der Einkaufkorb ist noch leer. </td>
</tr>
<!--Schleife für Tabellenzeilen mit "items"-->
<tr v-for="item in items" v-bind:class="{ inactive: item.isInactive }">
<td class="main bold"> {{ item.text }} </td>
<td class="main"><input v-model="item.number" class="number"> kg </td>
<td class="main bold">{{ item.number * item.price }} Euro</td>
<td class="main">Kilopreis: {{ item.price }}</td>
</tr>
<!--Summen-Zeile-->
<tr>
<td colspan="4" class="bold">
Gesamtpreis: {{ sum }} Euro
</td>
</tr>
</table>
<br/>
<!--Input "Menge in kg"-->
<input v-model="toAddQuant" class="number"> kg
<!--Dropdown "Obstsorte"-->
<select v-model="toAdd">
<option v-for="(item, index) in catalog" v-bind:value="index">{{ item.text }}</option>
</select>
<!--Button "Hinzufügen"-->
<button v-on:click="add">Hinzufügen</button>
</div>
<script>
var basket = new Vue({
el: '#basket',
data: {
//Warenkorbinhalt
items: [],
//verfügbare "items"
catalog: [
{text: 'Äpfel', price: 1.99, number: 1, isInactive: false},
{text: 'Bananen', price: 1.79, number: 1, isInactive: false},
{text: 'Kirschen', price: 2.49, number: 1, isInactive: false},
{text: 'Trauben blau', price: 2.39, number: 1, isInactive: false},
{text: 'Trauben grün', price: 2.29, number: 1, isInactive: false}
],
//hinzuzufügendes Element -> "Index" in catalog[]
toAdd: 0,
// hinzuzufügende Menge in kg
toAddQuant: 1,
},
computed: {
sum: function () {
var added = 0;
//iteriere über "items[]", addiere Produkt aus "price" und "number"
for (var i = 0; i < this.items.length; i++) {
added += this.items[i]['price'] * this.items[i]['number'];
}
return added;
}
},
watch: {
items: {
// korrigiere "number" < 0, setze "isInactive"
handler: function (items, itemsOld) {
for (var i = 0; i < basket.items.length; i++) {
if (items[i]['number'] < 0){
items[i]['number'] = 0;
}
if (items[i]['number'] == 0) {
items[i]['isInactive'] = true;
} else {
items[i]['isInactive'] = false;
}
}
},
deep: true
},
},
methods: {
add: function () {
//klone Objekt aus catalog[toAdd]
var newItem = Object.assign({}, this.catalog[this.toAdd]);
//number an toAddQuant anpassen
newItem['number'] = this.toAddQuant
this.items.push(newItem);
}
}
})
</script>
Dazu iteriert sie über die Einträge von Arrays, multipliziert Preis und Stückzahl der gespeicherten Objekte und summiert diese Produkte. Für die Anzeige der Summe dient ein simpler Verweis auf den Namen der Computed Property ({{ sum }}) im HTML-Block (Zeile 28), die den Summenwert per return übergibt.
Gut beobachtet
Beim Aktualisieren der Computed Properties geht Vue.js von der Seite der Daten aus: Es errechnet sum nicht bei jedem Zugriff, sondern nur, wenn sich Werte innerhalb des Objekts data verändern, etwa durch angebundene Input-Felder. Die Entwickler der Bibliothek nennen dies Reactivity [7] und weisen darauf hin, dass dies nur für direkt beim Instanzieren von Vue() übergebenen Attributen des Objekts data funktioniert.
Die Direktive v-if="item.number > 0" ist in dieser Version des Listings weggefallen, denn sie bewirkt, dass eine Zeile im Warenkorb verschwindet, wenn Sie den bisherigen Wert löschen. Für einen sinnvoll zu bedienenden Warenkorb graut der Code in Listing 2 Zeilen mit der Menge 0 lediglich aus.
Abbildung 4 zeigt einen Screenshot der Applikation: Die simplen CSS-Stile zu Beginn sind ebenso intuitiv wie HTML-Tags für die Tabelle. Beim Javascript-Code hat sich aber im Vergleich zu Listing 1 eine Menge getan. Das Array (Zeile 48) enthält nun keine Einträge; nach dem Laden erscheint ein leerer Warenkorb. Dafür enthält das Array catalog nun Einträge aus Objekten mit mehreren Feldern (Zeile 50 bis 56).

Abbildung 4: Mit dem Code aus Listing 2 ist es möglich, dem Warenkorb mit dem Button in der unteren Zeile Einträge aus einem Katalog hinzufügen. Nullmengen erscheinen ausgegraut.
Erst ein Klick auf Hinzufügen füllt den Warenkorb, und zwar mit der im linken Eingabefeld gewählten Menge des im Dropdown-Feld vor Hinzufügen gewählten Obsts. Dazu ist an das Dropdown ein Event Handler im Stil der Bibliothek gebunden, und zwar durch die Direktive v-on:click="add" (Zeile 40). Dieser feuert, wie leicht zu erraten, nach einem Mausklick die Methode add() ab (Zeile 94).
Methoden ohne Vue.js-spezifische Funktion binden Sie ans Feld methods im Konstruktor-Aufruf. Sie stehen dann für den Event Handler, aber auch für Template-Code innerhalb der geschweiften Klammern direkt im Root-Namensraum bereit.
Die Methode add entnimmt in ihrer ersten Zeile dem Array catalog das Objekt, das zur im Dropdown-Feld gewählten Obstsorte passt. Dann klont sie es, erstellt also ein neues Objekt mit gleichem Inhalt. Würden Sie es einfach per Gleichheitszeichen zuweisen, enthielte items mehrere Referenzen auf dasselbe Objekt, sobald Sie dem Warenkorb zwei Einträge für die gleiche Obstsorte hinzufügen.
In dem Fall wären dann etwa die Mengenangaben aller Zeilen mit Äpfeln gekoppelt. Mehrere Einträge der gleichen Sorte ergeben eigentlich keinen Sinn, doch die simple Anwendung verhindert solche mehrfachen Einträge nicht. Daher entkoppelt der Code alle Zeilen per Objekt-Klon.
Wie schon bisher hält Vue.js die Liste der Einkaufskorbeinträge innerhalb der per v-for="item in items"-Direktive definierten Schleife (Zeile 19-24) selbstständig aktuell. Die Methode add() wertet die per v-model an die Methoden toAdd und toAddQuant gebundenen HTML-Eingabefelder (Zeile 34 und 36) aus. Beim Feld für die Menge in Kilogramm handelt es sich um ein Input-Feld wie in Listing 1.
Das Dropdown-Feld bedarf noch einiger Kommentare. Da es als Select-Element angelegt ist, das Option-Elemente enthält, ist die Schleife v-for über das Array catalog (Zeile 37) folgerichtig (Abbildung 5). Zusätzlich zu den so erzeugten Einträgen für die Obstsorten gilt es aber sicherzustellen, dass im Datenfeld, das Sie mit v-model="toAdd" an das Select-Element gebunden haben, ein nullbasierter Indexwert landet: Mit dessen Hilfe wählen Sie die passende Zeile in der Form catalog[Index] aus.
![Abbildung 5: Die Option-Elemente im Select-Element entstehen durch eine Schleife über das Array <code>catalog[]</code>. Eine zusätzliche Variable <code>index</code> sorgt für nullbasierte Werte des Attributs <code>value</code>.](/wp-content/uploads/2018/12/b05-select-300x211.jpg)
Abbildung 5: Die Option-Elemente im Select-Element entstehen durch eine Schleife über das Array catalog[]. Eine zusätzliche Variable index sorgt für nullbasierte Werte des Attributs value.
Hierfür bietet Vue.js die Schreibweise (item, index) in catalog an (Zeile 37) [8], mit der in der Schleife außer dem Array-Eintrag item noch der Zähler index bereitsteht. Über v-bind:value="index" binden Sie ihn ans Value-Attribut des in der Schleife erzeugten Option-Elements, denn in HTML-Attributen funktioniert die Schreibweise mit den doppelten geschweiften Klammern nicht.
Die Methode add() benutzt den Wert der im Dropdown gewählten Option, um den Index-Eintrag aus catalog anzusprechen: this.catalog[this.toAdd] (Zeile 96). Mit Object.assign() vereinigen Sie dieses so ausgewählte Element mit dem leeren Objekt {}, was auf einen Klon hinausläuft. Zu beachten ist, dass Object.assign() erst seit rund vier Jahren in den Browsern bereitsteht, also nicht im Internet Explorer.
Bevor push() dieses neue Objekt zu items hinzufügt, passt der Code in Zeile 99 noch das Feld number dem per v-model="toAddQuant" angebundenen Mengenfeld vor dem Dropdown an. Die angezeigte Gesamtsumme (Zeile 28) hält Vue.js ebenfalls aktuell, egal, ob Sie neue Artikel hinzufügen oder die Menge bestehender im Feld kg verändern.
Bleibt nur noch das Attribut watch zu erläutern (Zeile 73): Über den Namen items koppeln Sie dessen Funktion handler() an das gleichnamige Feld in data. Das Framework ruft den Handler immer dann auf, wenn sich items verändert, und zwar mit dem neuen und alten Wert von items als Parameter. Das Attribut deep: true sorgt dafür, dass Vue.js beim Überwachen in die im Array enthaltenen Objekte hineinblickt. Deren Felder number steuert das Ausgrauen von Einträgen.
Die eigentliche Handler-Funktion (Zeile 76) ist schnell durchblickt: Beim Iterieren über alle items verändert sie zuerst eventuelle negative Werte für number zu null, denn negative Stückzahlen sind im Warenkorb verboten. Dann setzt sie das Feld isInactive in den items auf true oder false, je nachdem, ob die Stückzahl null ist oder nicht.
Bei v-bind:class="{ inactive: item.isInactive }" in der Schleife für die Einträge des Warenkorbs kommt dieser Wert dann zum Tragen und weist der Zeile in der Tabelle die CSS-Klasse inactive für ausgegraute Schrift zu, wenn der Wert des genannten Felds true lautet.
Fazit
Mit Vue.js koppeln Sie auf einfache Weise Benutzeroberflächen im Web mit griffigen in den HTML-Code eingebetteten Direktiven an einen Datenbestand. Dabei funktioniert das Anbinden beinahe magisch in beide Richtungen. Egal, ob Sie die Daten programmatisch oder per Eingabe in ein angebundenes Feld verändern: Stets aktualisiert die Bibliothek die verknüpften Elemente.
So wächst eine in einer Schleife erzeugte Liste oder Tabelle automatisch um eine Zeile, wenn zum zur Schleife gehörigen Array ein Eintrag hinzukommt. HTML-Attribute oder CSS-Klassen generieren Sie ebenfalls bei Bedarf aus gespeicherten oder errechneten Werten. Die sonst dafür nötigen umständlichen Event Handler oder DOM-Zugriffe kapselt das Framework.
Darüber hinaus bietet die Bibliothek die Möglichkeit, wiederverwendbare Komponenten [9] oder Animationen [10] einzusetzen. Es ist anders als mit Boliden wie React oder Angular möglich, ganze Anwendungen in eine einzige HTML-Datei mit Script-Element zu packen. Für größere Programme gibt es wie bei anderen Libraries ein Build-System, das Teildateien im letzten Schritt für schnelle Ladezeiten wieder zusammenfügt [11].
Glossar
-
Observer-Design-Muster
-
Statt regelmäßig nachzusehen, ob sich Daten geändert haben, registriert sich eine Komponente für eine automatische Nachricht.
Infos
-
JQuery: https://jquery.com
-
Vue.js: https://vuejs.org
-
Signal-Slot-Mechanismus in Qt: https://wiki.qt.io/New_Signal_Slot_Syntax/de
-
Vue.js einbinden: https://vuejs.org/v2/guide/installation.html
-
Moustache Template System: http://mustache.github.io
-
Computed Properties: https://vuejs.org/v2/guide/computed.html
-
Reactivity: https://vuejs.org/v2/guide/reactivity.html
-
Direktive v-for: https://vuejs.org/v2/api/#v-for
-
Components: https://vuejs.org/v2/guide/components.html
-
Animationen: https://vuejs.org/v2/guide/transitions.html
-
Build-System: https://vuejs.org/v2/guide/single-file-components.html





