© GoodStudio/Shutterstock.com
Advanced Vue - Teil 3

Anatomie einer Vue-Komponente


Auf wie viele verschiedene Arten kann ich eine Vue-Komponente entwickeln und warum sollte ich das tun? Wie funktioniert der Template Compiler? Kann ich meine Komponenten auch mit JSX realisieren und was sind Render-Funktionen? Viele Fragen, denen der vorliegende Artikel auf den Grund geht.

Obwohl der Begriff „Anatomie“ dem medizinischen Kontext entspringt, passt er auch sehr gut zu diesem Artikel. Bei Anatomie geht es um Wissensgewinn durch Analyse, Dekomposition und Durchleuchtung der zu erforschenden Sache. In Rahmen dieses Texts sollen Vue-Komponenten auseinandergenommen und die Funktionsweise von Vue soll nach außen sichtbar gemacht werden. Ziel dabei ist, dass der Leser zu Erkenntnissen gelangt, die bei der Entwicklung eigener Anwendungen helfen, sei es durch ein generelles, besseres Verständnis des Innenlebens von Vue oder durch den zusätzlichen Wissensgewinn hinsichtlich der unterschiedlichen Möglichkeiten, eine Vue-Komponente aufzubauen.

Einsteiger machen ihre erste Bekanntschaft mit Vue oft, indem man ihnen zeigt, wie man Vue in bestehende Projekte einbindet und wie vorhandenes Markup mit Vue angereichert werden kann. Listing 1 zeigt ein kleines Beispiel. Die neueste Vue-3-Version wird hier über den Link https://unpkg.com/vue@next geladen und steht somit im Projekt zur Verfügung. In Wirklichkeit ist dieser Link jedoch eine Weiterleitung und zeigt standardmäßig auf den globalen, vollständigen Build von Vue. Das kann man leicht prüfen, wenn man den Link direkt im Browser eingibt. Von Vue gibt es mehrere Builds:

  • vue.global.js

  • vue.runtime.global.js

  • vue.esm-browser.js

  • vue.runtime.esm-browser.js

Die ersten beiden Builds stellen alle Funktionen von Vue mit einem globalen Vue-Objekt zur Verfügung. Die nachfolgenden Dateien (*.esm-browser.js) sind für die Verwendung mit dem Modulsystem (ESM) gedacht. Wenn man mit einem Bundler oder Entwicklungswerkzeug wie Vue CLI oder Vite arbeitet, muss man als Entwickler nicht entscheiden, welche Variante eingebunden werden soll, das erfolgt automatisch im Hintergrund. Wenn man ohne Entwicklungswerkzeuge arbeitet und Vue direkt als Library verwenden möchte, muss man sich dagegen für eine der beiden Optionen entscheiden: global oder ESM.

Listing 1: Bestehendes Markup mit Vue „anreichern“ (Achtung: Falle!)

<!doctype html> <html lang="en"> <head> <script src="https://unpkg.com/vue@next"></script> <title>Listing 1</title> </head> <body> <div id="counter"> <h1>Counter: {{ counter }}</h1> </div> </body> <script> const Counter = { data() { return { counter: 0 } }, mounted() { setInterval(() => { this.counter++ }, 1000) } } Vue.createApp(Counter).mount('#counter') </script> </html>

Im Folgenden soll zunächst mit der globalen Version von Vue gearbeitet werden, weiter unten im Abschnitt „Vue mit ESM verwenden“ wird dann aber auch erklärt, wie das Zusammenspiel mit dem Modulsystem funktioniert. Alle hier genannten Build-Varianten gibt es selbstverständlich auch in einer Produktivversion (*.prod.js). Diese unterscheidet sich nur dadurch, dass sie für den produktiven Einsatz optimiert und minimiert ist.

Vue als globale Library verwenden

Den Unterschied zwischen *.global.js und *.runtime.global.js zu kennen, ist nicht ganz unerheblich. Listing 1 funktioniert tatsächlich nur, weil vue@next auf vue.global.js zeigt. Würde man in Listing 1 den Link beispielsweise auf <script src="https://unpkg.com/vue@3.0.0-rc.7/dist/vue.runtime.global.js"> ändern, würde er nicht mehr funktionieren. Ein Blick in die Konsole der Developer-Tools des Browsers gibt einen Hinweis auf das Problem. Man soll vue.global.js anstelle von vue.runtime.global.js verwenden, weil nur im globalen Build das Kompilieren von Markup zur Laufzeit möglich ist.

Wer Vue wie hier über ein CDN in sein Projekt einbindet und später auf Entwicklungswerkzeuge wie npm, Vue CLI oder Vite wechseln möchte, wird hier vielleicht ein Problem bekommen, denn alle genannten Entwicklungstools binden Vue im Standard nur mit der Runtime-Version ein. Der Grund hierfür ist einleuchtend: In größeren Vue-Projekten werden die Komponenten, die man entwickelt, normalerweise kompiliert. Ein vorheriges Kompilieren erzeugt optimierten JavaScript-Code, der direkt zur Laufzeit ausgeführt wird. Ein ständiges Kompilieren von Komponenten zur Laufzeit dagegen geht zu Lasten der Performance. Mit dem Konzept der Single File Components (SFC) bringt Vue außerdem ein eigenes Format zur Organisation und Entwicklung von Komponenten mit. Dabei handelt es sich um spezielle Source-Dateien (.vue), die aus separaten Blöcken mit Logik, HTML Markup (Template) und CSS bestehen. Hier geht es gar nicht ohne vorheriges Kompilieren. Das bedeutet: Vue-Anwendungen werden immer als JavaScript ausgeführt. Wenn im Rahmen des Entwicklungs- und Build-Vorgangs also die Komponenten schon kompiliert werden, muss zur Laufzeit der Compiler nicht vorhanden sein.

Vue-Komponente aus bestehendem Markup erzeugen

So einfach Listing 1 auch ist, zeigt es bereits die erste Variante, wie eine Komponente entstehen kann. In Vue 2 gab es mit der Property el die Möglichkeit, ein Vue-Objekt an bestehendes Markup zu binden. In Vue 3 wurde dieses Verhalten geändert. Man kann jetzt über die neue createApp-Methode beliebig viele sogenannte Root-Komponenten (dazu später noch mehr) erzeugen und über die mount-Methode an bestehendes Markup binden. Hierbei ist es wichtig zu verstehen, dass Vue einen klaren Unterschied macht, ob das Vue-Objekt, das man an createApp übergibt, über eine template-Property oder eine render-Funktion verfügt. Ist das (wie in Listing 1) nicht der Fall, geht Vue davon aus, dass das bestehende Markup aus dem angebundenen <div> (im Listing mit der ID counter) als Template anzusehen ist. Dann wird der Compiler aktiviert und automatisch eine sogenannte Render-Funktion erzeugt. Dabei ist es völlig egal, ob das Markup aus dem angebundenen <div> kommt, über die template-Property festgelegt wurde, oder ob die Komponente als SFC implementiert ist – am Ende steht immer eine Render-Funktion. Zur Laufzeit fügt sich eine Vue-Komponente durch Aufruf dieser Render-Funktion in das DOM ein. Das bedeutet in letzter Konsequenz auch, dass das <div> aus Listing 1 in Wirklichkeit gar nicht mit Vue angereichert wurde, sondern durch den Compiler zur Laufzeit in eine Vue-Komponente umgewandelt und somit komplett ersetzt wurde.

Templates

Mit den soeben gewonnenen Erkenntnissen kann man einen Schritt weiter gehen. Listing 2 zeigt eine Variante des ersten Beispiels. Das <div> bleibt weiterhin vorhanden, wird aber nicht mehr benutzt. Der Content wird jetzt aus der Property template gezogen. Im vorangegangenen Beispiel hat Vue diese Property implizit mit dem Markup aus dem angegebenen <div> befüllt, während hier explizit ein Template angegeben ist. Auch dieser Code hat wieder zur Folge, dass der Template Compiler aktiviert und das bestehende Markup im <div> komplett getauscht wird.

Das Template kann man wie in Listing 2 als einfachen String formulieren. Selbstverständlich kann man es auch als sogenannten „Template Literal“ oder „Template String“ angeben. Das ist ein JavaScript-Feature, das mit ES2015 [1] eingeführt wurde und mittlerweile von den meisten Browsern unterstützt wird. Im Gegensatz zum einfachen String wird ein Template String zwischen Backticks geschrieben, kann mehrzeilig sein und unterstützt Expressions. Da auch in diesem Fall der Compiler zum Einsatz kommen muss, um die Render-Funktion zu generieren, wird auch dieses Beispiel nur mit der zu Beginn erwähnten vue.global.js funktionieren.

Listing 2: Template-Property

... <body> <div id="counter"> <h1>Counter: {{ counter }}</h1> </div> </body> <script> const Counter = { template: '<h1>Template Counter: {{ counter }}</h1>', ...

Vue mit ESM verwenden

Im zweiten Teil dieser Artikelserie [2] wurde bereits darauf hingewiesen, dass man mit dem Standard ESM (ECMAScript Modules) auch gänzlich ohne Bundler auskommt. Wenn man eine Library-Version von Vue verwendet, die auf *.esm-browser.js endet, lässt sich Vue 3 mit dem Modulsystem benutzen und kommt (wenn man das mö...

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