mehr als defineModel und defineProps
Compiler-Makros wie defineProps und defineModel wirken wie normale Funktionsaufrufe, existieren zur Laufzeit aber gar nicht, sie werden vom Vue-Compiler beim Bauen des Codes wegtransformiert. Das dedizierte Vue Macros Plugin geht noch weiter und ergänzt eigene Makros für Reaktivität, Props-Weiterreichung und bedingte Kompilierung, mit klaren Kompromissen bei Tooling und Vorhersehbarkeit.
Inhaltsverzeichnis
- 1. Was ein Compiler-Makro überhaupt ist
- 2. Native Compiler-Makros in script setup
- 3. defineModel: Zwei-Wege-Bindung ohne Boilerplate
- 4. defineExpose und defineOptions im Detail
- 5. Das Vue Macros Plugin: Umfang und Installation
- 6. Reactivity Transform und $ref im historischen Kontext
- 7. Eigene Makros mit Vue Macros definieren
- 8. Grenzen, Tooling-Kosten und wann man darauf verzichtet
- 9. Native Makros vs. Vue Macros Plugin im Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Was ein Compiler-Makro überhaupt ist
Ein Compiler-Makro in Vue sieht aus wie ein ganz normaler Funktionsaufruf, existiert zur Laufzeit aber überhaupt nicht. Der Vue-Compiler erkennt bestimmte Funktionsnamen wie defineProps oder defineEmits innerhalb eines script setup-Blocks und ersetzt sie während der Kompilierung durch echten, ausführbaren JavaScript-Code. Zur Laufzeit im Browser gibt es diese Funktionen gar nicht, sie sind reine Compile-Time-Konstrukte, die dem Entwickler eine deklarative, knappe Syntax erlauben, ohne dass ein zusätzlicher Import nötig wäre.
Der entscheidende Unterschied zu einer normalen Funktion ist, dass ein Compiler-Makro Zugriff auf statische Informationen aus dem umgebenden Quellcode hat, etwa auf TypeScript-Typen, die zur Laufzeit längst verschwunden sind. defineProps<{ title: string }>() funktioniert nur, weil der Compiler den generischen Typparameter zur Kompilierzeit ausliest und daraus eine Laufzeit-Props-Deklaration generiert, eine normale Funktion könnte das nicht leisten, da TypeScript-Typen nach der Kompilierung nicht mehr existieren.
Vue selbst bringt bereits mehrere native Compiler-Makros mit, ohne dass ein zusätzliches Plugin installiert werden muss. Das separate Community-Projekt Vue Macros geht darüber hinaus und ergänzt weitere, nicht offizielle Makros, die zusätzliche Vite- oder Webpack-Plugins erfordern. Diese Unterscheidung zwischen nativen und plugin-basierten Compiler-Makros ist zentral, um zu verstehen, was in jedem Vue-3-Projekt sofort funktioniert und was zusätzliche Abhängigkeiten voraussetzt.
2. Native Compiler-Makros in script setup
Vue liefert im script setup-Kontext eine feste Menge nativer Compiler-Makros: defineProps, defineEmits, defineExpose, defineOptions, defineSlots und, seit Vue 3.4, defineModel. Diese Makros benötigen keinen Import, weil der Compiler sie speziell erkennt, ein regulärer Import würde sogar zu einem Fehler führen, da der Compiler explizit prüft, dass diese Namen nur als direkte Aufrufe im obersten Gültigkeitsbereich der Datei verwendet werden.
Diese Einschränkung ist beabsichtigt: Ein Compiler-Makro darf nicht in einer Bedingung, einer Schleife oder einer verschachtelten Funktion aufgerufen werden, weil der Compiler den Aufruf statisch zur Kompilierzeit analysieren muss, ohne den Code tatsächlich auszuführen. Wer versucht, defineProps innerhalb eines if-Blocks bedingt aufzurufen, erhält einen Kompilierfehler, weil diese Art von dynamischem Verhalten mit dem statischen Charakter eines Compiler-Makros unvereinbar ist.
// Native Vue compiler macros — no import required
// The compiler recognizes these names inside <script setup>
const props = defineProps<{
title: string
variant?: 'primary' | 'secondary'
}>()
const emit = defineEmits<{
(e: 'submit', payload: { id: number }): void
(e: 'cancel'): void
}>()
// defineOptions replaces the old export default { name: ... } block
defineOptions({
name: 'ConfirmDialog',
inheritAttrs: false,
})
// Wrong: macros cannot be called conditionally
// if (someFlag) {
// defineProps<{ foo: string }>() // compile error
// }
3. defineModel: Zwei-Wege-Bindung ohne Boilerplate
Vor Vue 3.4 erforderte eine Zwei-Wege-Bindung mit v-model in einer Kindkomponente eine Prop und ein passendes Emit-Event, meist modelValue und update:modelValue, plus einen berechneten Wert, der beides verbindet. Das defineModel-Makro fasst diesen wiederkehrenden Boilerplate in einer einzigen Zeile zusammen und gibt einen Ref zurück, der bei Änderung automatisch das passende Update-Event auslöst, ohne dass die Prop-Emit-Kopplung manuell nachgebaut werden muss.
defineModel unterstützt außerdem benannte Modelle für mehrere unabhängige Zwei-Wege-Bindungen in derselben Komponente, sowie einen required- und default-Modifikator analog zu defineProps. Für Formularkomponenten, die mehrere Werte gleichzeitig binden, etwa einen Datumsbereich mit Start- und Enddatum, reduziert dieses Makro spürbar die Menge an sich wiederholendem Code gegenüber der manuellen Prop-Emit-Lösung.
// DateRangePicker.vue — two independent two-way bindings via defineModel
const startDate = defineModel<string>('start', { required: true })
const endDate = defineModel<string>('end', { required: true })
function setToday() {
const today = new Date().toISOString().slice(0, 10)
startDate.value = today
endDate.value = today
}
// Parent usage:
// <DateRangePicker v-model:start="range.from" v-model:end="range.to" />
4. defineExpose und defineOptions im Detail
Weil script setup standardmäßig keine öffentliche Schnittstelle nach außen bereitstellt, jede lokale Variable ist nur intern in der Komponente sichtbar, existiert das Makro defineExpose speziell dafür, ausgewählte Methoden oder Werte gezielt für Template-Refs im Elternteil freizugeben. Ohne defineExpose bleibt eine Kindkomponente über einen Template-Ref praktisch nutzlos, weil die Elternkomponente sonst nur auf ein leeres Objekt zugreifen könnte.
defineOptions ergänzt das um Optionen, die klassisch im Options-API-Objekt standen, allen voran den Komponentennamen, der für rekursive Komponenten und für die Anzeige in den Vue DevTools relevant ist. Ohne defineOptions muss der Komponentenname aus dem Dateinamen erraten werden, was bei umbenannten oder umverteilten Dateien zu Inkonsistenzen führt. Beide Makros zusammen decken die Lücken ab, die durch den Wegfall des klassischen Options-API-Objekts in script setup entstehen.
5. Das Vue Macros Plugin: Umfang und Installation
Über die nativen Makros hinaus existiert das Community-Projekt Vue Macros, ein Vite- beziehungsweise Webpack-Plugin, das den script setup-Compiler um zusätzliche, nicht offizielle Makros erweitert. Dazu zählen unter anderem defineProp für einzelne, reaktive Props als separate Refs, $ als Kurzform für berechnete Werte und experimentelle Erweiterungen wie bedingte JSX-Kompilierung. Vue Macros wird über npm install -D unplugin-vue-macros installiert und als Vite-Plugin registriert.
Wichtig ist die Abgrenzung: Alles, was Vue Macros bereitstellt, ist kein offizieller Bestandteil von Vue selbst, sondern eine zusätzliche Kompilierstufe, die vor dem eigentlichen Vue-Compiler läuft. Das bedeutet konkret, dass jedes Projekt, das Vue Macros nutzt, eine zusätzliche Build-Abhängigkeit hat und dass Editor-Tooling wie Volar diese zusätzliche Syntax nur mit passenden Erweiterungen korrekt versteht.
// vite.config.ts — enabling the Vue Macros plugin
import { defineConfig } from 'vite'
import Vue from '@vitejs/plugin-vue'
import VueMacros from 'unplugin-vue-macros/vite'
export default defineConfig({
plugins: [
VueMacros({
plugins: {
vue: Vue(),
},
}),
],
})
// Component.vue — using the non-official defineProp macro
// One reactive prop as its own ref, instead of destructuring defineProps()
const title = defineProp<string>('title')
6. Reactivity Transform und $ref im historischen Kontext
Ein wichtiges Kapitel in der Geschichte der Vue-Compiler-Makros ist Reactivity Transform, ein experimentelles Feature, das mit Makros wie $ref, $computed und $$ versuchte, das explizite .value bei Refs überflüssig zu machen. Das Vue-Core-Team hat Reactivity Transform in Vue 3.4 als offiziell experimentelles Feature wieder entfernt, mit der Begründung, dass die implizite Ref-Auflösung zu viel Compiler-Magie erforderte und die Nachvollziehbarkeit von Code litt, der auf den ersten Blick wie einfache Variablen aussah, sich zur Laufzeit aber wie reaktive Refs verhielt.
Das Vue Macros Plugin unterstützt diese Syntax weiterhin für Teams, die bewusst darauf setzen wollen, allerdings außerhalb des offiziellen Vue-Kerns. Wer heute ein neues Projekt startet, sollte sich dieser Historie bewusst sein: $ref-Syntax ist kein Standard-Vue-Feature mehr, sondern eine Vue-Macros-spezifische Erweiterung, die zusätzliche Editor-Unterstützung benötigt und beim Onboarding neuer Teammitglieder erklärungsbedürftig bleibt.
7. Eigene Makros mit Vue Macros definieren
Vue Macros erlaubt es fortgeschrittenen Nutzern, eigene Compiler-Makros zu definieren, etwa für projektspezifische Muster, die sich wiederholt in vielen Komponenten finden. Ein typisches Beispiel ist ein Makro, das automatisch Logging-Code um eine Funktion herum generiert, basierend auf einer Konfiguration, die zur Kompilierzeit ausgewertet wird, ohne dass diese Logik in jeder einzelnen Komponente manuell wiederholt werden muss.
Diese Möglichkeit ist mächtig, bringt aber Verantwortung mit sich: Ein eigenes Compiler-Makro verändert den tatsächlich ausgeführten Code auf eine Weise, die im Editor nicht sichtbar ist, ohne die Kompilierausgabe explizit zu betrachten. Für kleinere Teams oder Projekte mit hoher Fluktuation ist der Aufwand, eigene Makros zu pflegen und zu dokumentieren, häufig größer als der Nutzen gegenüber einer einfachen, expliziten Utility-Funktion ohne Compiler-Magie.
8. Grenzen, Tooling-Kosten und wann man darauf verzichtet
Der größte praktische Nachteil von Compiler-Makros, egal ob nativ oder aus dem Vue Macros Plugin, ist die Abhängigkeit von korrektem Editor-Tooling. Ohne die passende Volar-Erweiterung interpretiert der TypeScript-Sprachserver defineProps und defineModel nicht korrekt, was zu falschen Fehlermeldungen im Editor führt, selbst wenn der eigentliche Build fehlerfrei durchläuft. Bei zusätzlichen, nicht offiziellen Makros aus dem Vue Macros Plugin verschärft sich dieses Problem, weil weniger verbreitete Editor-Erweiterungen diese Syntax mitunter gar nicht kennen.
Ein zweiter Nachteil betrifft die Nachvollziehbarkeit für neue Teammitglieder. Native Vue-Compiler-Makros sind inzwischen weit verbreitetes Wissen und in der offiziellen Dokumentation gut erklärt, zusätzliche Vue-Macros-Erweiterungen wie defineProp oder eigene, projektspezifische Makros erfordern hingegen zusätzliche Einarbeitung. Die Empfehlung für die meisten Teams: native Compiler-Makros konsequent nutzen, das Vue Macros Plugin nur gezielt für einzelne, klar begründete Fälle einsetzen, nicht als generelle Standardausstattung jedes neuen Projekts.
9. Native Makros vs. Vue Macros Plugin im Vergleich
Die folgende Tabelle stellt native Vue-Compiler-Makros dem zusätzlichen Vue Macros Plugin gegenüber, um die Entscheidung für oder gegen die zusätzliche Abhängigkeit zu erleichtern.
| Kriterium | Native Compiler-Makros | Vue Macros Plugin | Empfehlung |
|---|---|---|---|
| Zusätzliche Abhängigkeit | Keine, Teil von Vue selbst | unplugin-vue-macros nötig | Native Makros bevorzugen |
| Editor-Unterstützung | Volar unterstützt es standardmäßig | Teilweise eingeschränkt | Vor Einsatz Editor-Support prüfen |
| Onboarding neuer Entwickler | Gut dokumentiert, verbreitet | Erfordert Zusatzwissen | Nur bei klarem Team-Konsens |
| Funktionsumfang | defineProps, defineModel, defineExpose | Zusätzlich defineProp, $, eigene Makros | Nur bei konkretem Zusatzbedarf |
Die Tabelle macht deutlich, dass native Compiler-Makros für die meisten Vue-3-Projekte ausreichen. Das Vue Macros Plugin lohnt sich vor allem für Teams, die bewusst experimentelle Syntax evaluieren wollen oder sehr spezifische, wiederkehrende Muster über eigene Makros standardisieren möchten.
Mironsoft
Vue 3, script setup und moderne Komponentenarchitektur
Unsicher, ob Vue Macros für euer Projekt sinnvoll ist?
Wir bewerten für euer Vue-Projekt, ob native Compiler-Makros ausreichen oder ob das Vue Macros Plugin einen konkreten Mehrwert bietet, inklusive Editor-Tooling-Check und Migrationsplan für bestehende Komponenten.
Compiler-Makro-Audit
Bestehenden script-setup-Code auf saubere Makro-Nutzung prüfen
defineModel-Migration
Alte Prop-Emit-Bindungen auf defineModel umstellen
Tooling-Check
Volar-Kompatibilität und Editor-Support vor Einführung prüfen
10. Zusammenfassung
Vue Macros, verstanden als Sammelbegriff für Compiler-Makros in Vue, reichen von den nativen, offiziellen Makros wie defineProps, defineModel und defineExpose bis zum separaten Community-Plugin, das zusätzliche, nicht offizielle Syntax wie defineProp und eigene, projektspezifische Makros ermöglicht. Native Makros sind für die überwiegende Mehrheit der Vue-3-Projekte ausreichend und benötigen keine zusätzliche Build-Abhängigkeit.
Das zusätzliche Vue Macros Plugin lohnt sich nur, wenn ein Team bewusst experimentelle Syntax evaluieren will oder wiederkehrende, projektspezifische Muster über eigene Compiler-Makros standardisieren möchte. Wichtig bleibt in jedem Fall die Prüfung, ob das Editor-Tooling, allen voran Volar oder der Vue Language Server, die verwendeten Makros korrekt versteht, sonst entstehen falsche Fehlermeldungen trotz fehlerfreiem Build.
Vue Macros und Compiler-Makros, das Wichtigste auf einen Blick
Native Makros
defineProps, defineEmits, defineModel, defineExpose, defineOptions, ohne Import nutzbar.
defineModel seit 3.4
Ersetzt manuelle Prop-Emit-Kopplung für v-model in einer einzigen Zeile.
Vue Macros Plugin
unplugin-vue-macros ergänzt defineProp, $ und eigene Makros, mit Tooling-Kosten.
Reactivity Transform
Wurde aus dem Vue-Core entfernt, lebt nur noch als Vue-Macros-Erweiterung weiter.