Komplexe Webanwendungen mit Vue.js, Teil 1
Seite 5: Funktionale Komponenten
Obwohl Vue.js grundsätzlich sehr performant ist, belegt jede zusätzliche Komponente Speicherressourcen. Nicht jede Komponente benötigt aber eine eigene Vue-Instanz und Change Detection. Der Einsatz funktionaler Komponenten kann die Ressourcen dafür sparen.
Zur Laufzeit erhalten funktionale Komponenten keine Vue-Instanz. Vielmehr verhalten sich derartige Komponenten als wären sie Teil der übergeordneten Komponente. Funktionale Komponenten eignen sich daher besonders für einfache und generische UI-Bausteine wie Buttons, Listenelemente oder Dropdown-Controls. Ein praktisches Beispiel einer funktionalen Komponente ist der Platzhalter für Routing-Views <router-view/>.
Man kann funktionale Komponenten durch das Ergänzen des Attributs functional im Template-Tag mit einem Template erstellen. Häufig realisieren Entwickler funktionale Komponenten allerdings mit eigenen Renderfunktionen.
Eine funktionale Komponente hat keine eigene Instanz und daher auch keinen this-Kontext. Um auf Properties, Ereignishandler und andere Definitionen zuzugreifen, erhält eine funktionale Komponente ein Objekt context.
Eine beispielhafte Renderfunktion ist als funktionale Komponente wie folgt aufgebaut:
export default {
name: 'HelloList',
functional: true,
props: {
languages: Array
},
render(h, context) {
return h("ul", context.props.languages.map(language => h("li", language)))
}
}
Die Renderfunktion erhält das Objekt context als zweites Attribut und der Zugriff auf das Array erfolgt nun nicht mit this.languages, sondern mit context.props.languages.
Slots und Scoped Slots
In größeren Anwendungen ist es immer eine Herausforderung, Redundanz zu vermeiden. Man sollte daher anstreben, möglichst generische Komponenten zu entwickeln. Ein wichtiges Werkzeug dafür sind Slots. In ihrer einfachen Form sind Slots Platzhalter innerhalb des Templates einer Komponente. Die übergeordnete Komponente kann sie mit Inhalt füllen. So kann zum Beispiel eine Card-Component den eigentlichen Inhalt als Slot definieren:
<template>
<div class="card">
<slot/>
</div>
</template>
In der Komponente definiert man der Inhalt innerhalb des Elements:
<card>
<h2>Vue.js</h2>
<p>The Progressive JavaScript Framework</p>
</card>
Es ist auch möglich, mehrere Slots zu definieren. Sie müssen dann aber unterschiedliche Namen tragen.
Die Parent-Komponente definiert den Inhalt der Slots und läuft im Kontext der Komponente ab. Mit einem Slot-Scope ist der Datenaustausch zwischen den beiden Komponenten möglich. Das folgende Beispiel übergibt eine Referenz auf eine Funktion (close) und einen String an den Slot-Scope (slogan):
<template>
<div class="card">
<slot :closeFunction="close"
slogan="The Progressive JavaScript Framework"/>
</div>
</template>
Die ĂĽbergeordnete Komponente kann nun darauf zugreifen, indem ĂĽber das Attribut *slot-scope* ein Name fĂĽr den Scope vergeben wird:
<card>
<template slot-scope="cardScope">
<h2>Vue.js</h2>
<p>{{ cardScope.slogan }}</p>
<button @click="cardScope.closeFunction()">Close</button>
</template>
</card>
Die Komponente kann nun über cardScope die close-Funktion innerhalb der Card-Komponente aufrufen und den Slogan aus ihr empfangen. Scoped Slots ermöglichen elegante generische Komponenten.