Zwei Schreibweisen in Vue

Composition oder Options: was hält im Team länger?

Die Wahl entscheidet weniger über den Funktionsumfang als darüber, wie leicht sich Logik teilen lässt und wie viel TypeScript ohne Nachhilfe erkennt.

KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt
Seit 1997 am Markt Kleine Gruppen Präsenz und Live-Online Zertifizierte Trainer
Worum es geht

Verstreute Logik kostet über die Jahre mehr Zeit als die Umstellung selbst

Der typische Fall ist eine Listenansicht mit Suchfeld: Der Suchbegriff steht in data, die Verzögerung vor der Abfrage in methods, das Auslösen in watch, der erste Aufruf in created und die aufgeräumte Ausgabe in computed. Fünf Blöcke für ein Anliegen. Kommt eine Sortierung dazu und ein gespeicherter Filter, sind es fünfzehn Fragmente, verteilt über eine Datei, in der man beim Ändern ständig springt.

Teuer wird das nicht beim Schreiben, sondern beim Ändern durch jemand anderen. Wer die Verzögerung von 300 auf 500 Millisekunden setzen soll, muss erst herausfinden, welche der vier Methoden überhaupt noch aufgerufen wird. Bei Mixins kommt eine zweite Frage dazu: Woher stammt eigentlich das Feld laedt, das im Template steht? Die Datei sagt es nicht, weil ein Mixin seine Felder wortlos in die Komponente hineinreicht.

Der naheliegende Ausweg, das ganze Projekt umzuschreiben, hat einen eigenen Preis. Er bringt fachlich null, macht jede offene Änderung konfliktreich und nimmt dem Team die Lesegewohnheiten, mit denen es bisher schnell war. Die Entscheidung lautet deshalb nicht alt gegen neu, sondern: welche Datei fasst du an und wann.

KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt
Der direkte Vergleich

Der direkte Vergleich

Options API

Die Komponente ist ein Objekt mit festen Fächern für data, computed, methods, watch und Lebenszyklus

Composition API

Der gesamte Zustand entsteht in einem zusammenhängenden Block, meist im Bereich script setup

Wo steht die Logik zu einem einzelnen Anliegen?

Options API

Verteilt auf bis zu fünf Optionen. Wer das Suchfeld ändert, springt zwischen data, computed, watch, methods und created hin und her.

Composition API

Untereinander in einem Block. Zwei Anliegen ergeben zwei Blöcke, die sich sogar mit einer Leerzeile und einem Kommentar trennen lassen.

Wie teilt ihr Logik zwischen mehreren Komponenten?

Options API

Über Mixins. Die reichen ihre Felder ohne sichtbaren Bezug in die Komponente, und bei gleichen Namen entscheidet die Reihenfolge, welches Feld gewinnt.

Composition API

Über Composables, also gewöhnliche Funktionen. Der Import nennt die Herkunft, die Rückgabe nennt den Inhalt, und einen Namenskonflikt löst du beim Auspacken mit einem anderen Namen.

Wie schnell findet sich jemand zurecht, der Vue neu lernt?

Options API

Die Fächer sind vorgegeben, jede Komponente sieht gleich aus. Wo etwas hingehört, muss niemand entscheiden.

Composition API

Die Reihenfolge im Block ist frei. Ohne eine Absprache im Team wuchern Dateien, in denen refs, Funktionen und Watcher beliebig gemischt stehen.

Wie viel erkennt TypeScript ohne zusätzliche Angaben?

Options API

Props funktionieren über das Laufzeitobjekt, aber this in einer Methode muss der Compiler über die gesamte Optionsstruktur herleiten. Bei Mixins und verschachtelten computed wird das lückenhaft.

Composition API

Jeder ref trägt seinen Typ, Composables geben ihn weiter, und defineProps nimmt den Typ direkt entgegen. Es gibt kein this, dessen Form erst zusammengesucht werden muss.

Wie leicht laufen stille Reaktivitätsfehler durch?

Options API

this.menge ist im Skript und im Template dieselbe Zahl. Es gibt kein .value, das man vergessen kann, und keine Destrukturierung, die die Verbindung kappt.

Composition API

Ein Ref ist im Skript ein Objekt und im Template der Wert. Eine Abfrage ohne .value ist immer wahr, weil ein Objekt immer wahrheitswertig ist, und keine Meldung weist darauf hin.

Was kostet die Umstellung eines bestehenden Bestands?

Options API

Nichts. Vue 3 unterstützt sie vollständig, der Bestand läuft weiter, und neue Komponenten dürfen trotzdem anders geschrieben sein.

Composition API

Je Datei überschaubar, in Summe spürbar. Teuer sind nicht die Komponenten, sondern die Mixins, weil an ihnen mehrere Dateien gleichzeitig hängen.

Was passt wann

Ihr fangt eine neue Anwendung an oder legt ein größeres Feature neu an
Composition API mit script setup, und Logik, die ein zweites Mal gebraucht wird, wandert sofort in ein Composable statt in ein Mixin.
Ein gewachsenes Projekt läuft stabil und das Team kennt die Options API
Bestand lassen, neue Komponenten in der Composition API schreiben und nur die Dateien umbauen, die aus fachlichen Gründen ohnehin geöffnet werden.
Ihr stolpert regelmäßig über Mixins, deren Felder niemand mehr zuordnen kann
Genau diese Mixins zuerst in Composables überführen und die aufrufenden Komponenten ansonsten unangetastet lassen.

Fünf Fragen, und die Schreibweise steht fest

  1. 01 Enthält die Komponente mehr als ein Anliegen? Dann Composition API.
  2. 02 Soll dieselbe Logik in einer zweiten Komponente laufen? Dann wird sie ein Composable.
  3. 03 Sucht ihr regelmäßig, aus welchem Mixin ein Feld stammt? Dann lohnt der Umbau.
  4. 04 Braucht TypeScript in der Komponente ständig Hilfstypen? Dann Composition API.
  5. 05 Ist es eine Anzeigekomponente mit drei Props? Dann bleibt die Options API kürzer.
Was du mitnimmst

Was du danach ohne langes Abwägen entscheidest

Die beiden Schreibweisen erzeugen dieselbe Komponente. Sie unterscheiden sich darin, wo der Zustand entsteht, wie Logik zwischen Komponenten wandert und wie viel TypeScript ohne zusätzliche Typangaben erkennt. Wer diese drei Punkte einmal an einer echten Komponente durchgespielt hat, entscheidet den Rest in Minuten.

Zusammengehörendes zusammen halten

In einem Block mit script setup stehen ref, computed, watch und onMounted zu einem Anliegen direkt untereinander. Beim Ändern liest du zehn Zeilen am Stück statt an vier Stellen der Datei zu suchen.

Geteilte Logik als Composable ausziehen

Statt eines Mixins schreibst du eine normale Funktion, die refs und Methoden zurückgibt. Am Aufruf steht, was aus ihr kommt, und der Import sagt, aus welcher Datei. Namenskonflikte werden dabei zu einer sichtbaren Entscheidung statt zu einem stillen Überschreiben.

Die drei Stellen kennen, an denen Reaktivität verloren geht

Ein vergessenes .value im Skript, eine Destrukturierung eines reactive-Objekts und ein watch auf einen bereits ausgelesenen Wert. Alle drei erzeugen keine Fehlermeldung, sondern nur ein Template, das sich nicht mehr aktualisiert.

Props und Events über Typen statt über Laufzeitobjekte beschreiben

defineProps mit einem Typparameter macht die TypeScript-Definition zur einzigen Quelle. Der Compiler erzeugt daraus die Laufzeitangaben, du pflegst nicht zwei Listen, die auseinanderlaufen können.

Schrittweise umstellen statt in einem Rutsch

Eine Datei nach der anderen, und nur die, die ohnehin geändert wird. Beide Schreibweisen laufen im selben Projekt, im selben Router und mit demselben Store nebeneinander.

Wissen, wann du eine Komponente in Ruhe lässt

Eine Anzeigekomponente mit drei Props und einer computed gewinnt durch den Umbau nichts. Der Nutzen entsteht dort, wo mehr als ein Anliegen in derselben Datei wohnt.

KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt

Dieselbe Komponente, zweimal geschrieben

In der Options API sieht die Suchliste so aus: data liefert suchbegriff, treffer und laedt, watch beobachtet suchbegriff und ruft die Methode suchen auf, methods enthält suchen mit dem Aufruf an die Schnittstelle, created stößt den ersten Durchlauf an, und computed rechnet die Zahl der Treffer aus. Fünf Optionen, jede an einer anderen Stelle der Datei, verbunden nur über this.

In der Composition API stehen dieselben Zeilen untereinander: const suchbegriff = ref(''), const treffer = ref([]), const laedt = ref(false), darunter die Funktion suchen, darunter watch(suchbegriff, suchen) und onMounted(suchen). Kommt eine Sortierung dazu, entsteht ein zweiter Block darunter statt fünf neuer Einsprengsel in bestehenden Optionen. Das Template ändert sich bei diesem Umbau nicht, weil im Template ein Ref automatisch ausgepackt wird.

Beide Fassungen erzeugen dieselbe Komponente, und Vue mischt sie im selben Projekt problemlos. Deshalb ist die Umstellung eine Frage pro Datei und nie eine Frage für das ganze Repository. Was du vermeiden solltest, ist die Mischung innerhalb einer Datei: setup neben data und methods funktioniert zwar, aber in setup gibt es kein this, und die Lesereihenfolge wird schlechter als in beiden reinen Formen.

Mixins gegen Composables

Ein Mixin wird in die Komponente hineingemischt. Seine data-Felder, Methoden und Lebenszyklushaken landen so in der Komponente, als hätte man sie dort geschrieben. Steht im Template laedt, sagt die Datei nicht, woher das kommt. Definieren zwei Mixins beide ein Feld laedt, gewinnt das später eingebundene, und eine gleichnamige Option in der Komponente selbst gewinnt gegen beide. Es gibt keine Warnung, nur einen Wert, der anders ist als erwartet.

Ein Composable ist dagegen eine gewöhnliche Funktion, die refs und Funktionen zurückgibt. Die Zeile const { laedt, treffer, suchen } = useSuche(endpunkt) nennt die Herkunft im Import und den Inhalt im Auspacken. Zwei Composables, die beide ein laedt liefern, kollidieren nicht still, sondern zwingen dich zu const { laedt: laedtSuche } = useSuche(...). Aus dem stillen Überschreiben wird eine Zeile, die jemand geschrieben hat.

Eine Falle gibt es dabei: Lebenszyklushaken wie onMounted müssen synchron während des Aufbaus registriert werden. Ein Composable, das erst nach einem await ein onMounted aufruft, findet keine aktive Komponenteninstanz mehr und hängt sich an nichts. Der Aufruf schlägt nicht laut fehl, der Haken läuft einfach nie. Deshalb gehören Datenabrufe in ein Composable hinein, die Registrierung von Haken aber immer vor das erste await.

Was TypeScript tatsächlich mitbekommt

In der Options API beschreibst du Props als Laufzeitobjekt, etwa menge mit type Number und required true. TypeScript leitet daraus einen Typ ab, solange die Komponente in defineComponent verpackt ist. Anstrengend wird es bei this: Der Compiler muss aus data, computed, methods, props und allen Mixins ein Objekt zusammensetzen. Solange alles in einer Datei steht, klappt das gut. Sobald Mixins ins Spiel kommen, fallen Felder aus der Ableitung heraus und stehen als any da.

In der Composition API ist der Typ die Quelle: defineProps mit einem Typparameter beschreibt menge als number und titel als optionalen string, und der Compiler erzeugt daraus die Laufzeitangaben. Dasselbe gilt für defineEmits, wo du die erlaubten Ereignisnamen samt Argumenttypen hinschreibst und beim Auslösen sofort eine Meldung bekommst, wenn der Name nicht stimmt. Refs tragen ihren Typ durch Composables hindurch, ohne dass du ihn ein zweites Mal notierst.

Eine Einschränkung bleibt: Die Typen in defineProps müssen für den Compiler auflösbar sein. Bei einfachen und bei importierten Typen ist das in aktuellen Vue-Versionen der Normalfall, bei zusammengesetzten Typen aus mehreren Dateien lohnt sich vor der Entscheidung ein kurzer Test in einer Wegwerfkomponente. Was dort nicht funktioniert, funktioniert auch im Projekt nicht.

Die drei Stellen, an denen Reaktivität still verloren geht

Erstens das .value. Im Skript ist ein Ref ein Objekt mit einer Eigenschaft value, im Template packt Vue es automatisch aus. Wer if (suchbegriff) statt if (suchbegriff.value) schreibt, bekommt keinen Fehler, sondern immer den wahren Zweig, weil ein Objekt immer wahrheitswertig ist. Das ist der häufigste Umstiegsfehler und der am schwersten zu sehende.

Zweitens die Destrukturierung. const { menge } = reactive({ menge: 0 }) liefert die Zahl 0, nicht die Verbindung zum Proxy. Ändert sich menge später, merkt davon niemand etwas. toRefs löst das, indem es jedes Feld in ein eigenes Ref verpackt. Wer diese Falle nicht ständig umgehen will, arbeitet mit ref statt reactive und hat nur eine Regel statt zweier.

Drittens die Quelle eines watch. watch(zustand, ...) auf ein reactive-Objekt beobachtet tief. watch(() => zustand.menge, ...) beobachtet genau ein Feld, weil die Getter-Funktion bei jeder Auswertung neu liest. watch(zustand.menge, ...) dagegen übergibt eine Zahl und reagiert nie, weil eine Zahl keine Quelle sein kann; in der Entwicklung meldet Vue das immerhin als ungültige Quelle in der Konsole, im Produktionsbau bleibt es still. Alle drei Varianten sehen fast gleich aus, und nur eine davon tut, was du meinst.

Wann ein Bestand bleibt, wo er ist

Die Options API ist in Vue 3 nicht abgekündigt. Sie wird vom Compiler vollständig unterstützt, und es gibt keine Ankündigung, sie zu entfernen. Wer also einen laufenden Auftritt hat, steht nicht unter Zeitdruck. Der Druck entsteht anderswo: bei Vue 2 selbst, dessen offizielle Wartung ausgelaufen ist, was aber eine Versionsfrage ist und keine Frage der Schreibweise.

Ein sinnvolles Kriterium für den Umbau einer Datei hat drei Teile: Sie wird aus fachlichen Gründen ohnehin geöffnet, sie enthält mehr als ein Anliegen, und ihre Logik wird an einer zweiten Stelle gebraucht. Trifft nur der erste Punkt zu, reicht es, die neue Funktion in der vorhandenen Schreibweise zu ergänzen. Treffen alle drei zu, ist der Umbau billiger als die nächste Suche nach dem Ursprung eines Feldes.

Für neue Dateien lohnt eine Absprache im Team über die Reihenfolge im Block, weil die Composition API sie nicht vorgibt: erst Props und Emits, dann der Zustand, dann abgeleitete Werte, dann Funktionen, dann Watcher und Lebenszyklus. Das kostet eine Zeile in der Codekonvention und erspart die Diskussion in jedem zweiten Review.

Dazu passende Kurse

In den Vue-Kursen bei cmt siehst du zwei Schreibweisen an derselben Komponente und entscheidest danach an eigenem Code, welche für euch trägt.

Weil ein großer Teil der Umstiegsfehler aus Sprachdetails wie Destrukturierung und Referenzen kommt, lohnt es sich, vorher die Grundlagen von JavaScript und TypeScript auffrischen .

Wissen prüfen

Wie sicher bist du beim Thema wirklich?

Lesen fühlt sich schnell nach Können an. Ein kurzer Test zeigt dir, was davon schon sitzt und wo sich ein Kurs lohnt. Kostenlos, ohne Anmeldung, mit einer Erklärung zu jeder Antwort.

Der passende Lernpfad

Wenn du nicht nur ein Thema abhaken, sondern eine Rolle ausfüllen willst, zeigt dir der Lernpfad die Kurse in der Reihenfolge, in der sie aufeinander aufbauen.

Karrierepfad

Dein Weg zum Vue.js Entwickler

Vue.js nur halbwegs zu können, das reicht einfach nicht für professionelle skalierbare Frontends. 

Dieser Karrierepfad bringt dich schnell auf professionelles Niveau. Du lernst Vue.js in logisch aufgebauten Modulen – vom Fundament bis zu fortgeschrittenen Patterns. Du verstehst Konzepte wie Komponenten-Architektur, State-Management oder Reaktivität so, dass du selbstständig sauberen Code schreibst. - Starte jetzt und entwickle mit Vue.js elegante, performante Anwendungen!

Ein sehr gutes, praxisorientiertes und nachhaltiges Seminar. So sollte es immer sein. Vielen Dank.
LimeSurvey - Anwendertraining Teil 1 (Grundlagen)
Die Schulung ist gerade für Einsteiger oder Entwickler*innen mit eingestaubtem Basiswissen sehr hilfreich.
Vue.js 3 Grundkurs
Ich bin sehr glücklich Franz als Trainer gehabt zu haben. Er hat offensichtlich unglaublich viel Wissen zu den Themen, und schafft es, dieses auch interaktiv und verständlich weiterzugeben.
React Komplettausbildung

Häufige Fragen

Ist die Options API abgekündigt?
Nein. Beide Schreibweisen sind fester Bestandteil von Vue 3 und laufen im selben Projekt nebeneinander, sogar im selben Verzeichnis. Die Dokumentation empfiehlt die Composition API für größere Anwendungen, weil sich Logik dort besser bündeln und teilen lässt, führt die Options API aber weiterhin als vollwertige Wahl. Eine Ankündigung, sie zu entfernen, gibt es nicht.
Brauche ich script setup für die Composition API?
Nein, setup als normale Option funktioniert ebenfalls. Der Unterschied: Dort musst du alles, was das Template sehen soll, ausdrücklich zurückgeben, und die Compiler-Makros defineProps und defineEmits mit reinen Typangaben stehen nicht zur Verfügung. Für neue Komponenten ist script setup die kürzere Form derselben Sache, für den schrittweisen Umbau einer bestehenden Datei ist setup als Option manchmal der kleinere Schritt.
Kann ich Mixins und Composables gleichzeitig nutzen?
Ja, und beim Umbau ist genau das der Weg. Ein Composable wird in setup aufgerufen, ein Mixin läuft davon unabhängig weiter. Du ziehst also ein Mixin nach dem anderen um. Aufpassen musst du nur, solange beide dasselbe Feld liefern: Dann existiert der Name zweimal, und im Template gewinnt der Wert aus setup, weil Vue zuerst dort nachsieht und erst danach in data. Das Mixin bleibt also stehen und wirkt nicht mehr, und diese Kombination räumst du besser sofort auf, statt sie als Zwischenstand liegen zu lassen.
Woran merke ich, dass eine Komponente zu groß geworden ist?
An der Zahl der Sprünge beim Ändern. Wenn du für eine einzige fachliche Änderung an mehr als zwei Stellen der Datei musst, trägt die Komponente mehr als ein Anliegen. Das ist unabhängig von der Zeilenzahl: 400 Zeilen mit einem Anliegen sind lesbar, 120 Zeilen mit vier Anliegen sind es nicht.
Persönlich für dich da

Deine Ansprechpartner

Du bist dir nicht sicher, welcher Kurs oder welches Level zu dir passt? Wir beraten dich persönlich und kostenlos.

Yves Hoppe

Yves Hoppe

Weiterbildung & Beratung

Hilft dir, aus dem Webentwicklung-Programm den passenden Kurs für deinen Stand zu finden.

Norbert Jansen

Norbert Jansen

Beratung & Inhouse

Plant mit dir Inhouse-Trainings, die auf eure Abläufe und euren Datenbestand zugeschnitten sind.

Vue-Komponenten, die man in einem halben Jahr noch lesen kann

In den Vue-Kursen bei cmt baust du dieselbe Komponente in beiden Schreibweisen auf und ziehst die geteilte Logik anschließend in eigene Composables.