jede relevante Änderung mit Vorher-Nachher-Code
Vue 3 Breaking Changes verstecken sich selten im offensichtlichen Code, sondern in v-model-Syntax, entfernten Filtern, umgebauter Global API und dem gestrichenen Event Bus. Diese Checkliste arbeitet die wichtigsten Änderungen systematisch ab, jede mit konkretem Vorher-Nachher-Beispiel, damit bei der Migration nichts übersehen wird.
Inhaltsverzeichnis
- 1. Warum eine Checkliste besser ist als Trial-and-Error
- 2. v-model: neue Syntax und mehrere Bindungen
- 3. Global API: aus Vue.use() wird createApp()
- 4. Entfernte Filter: Ersatz durch computed und Methoden
- 5. Event Bus entfernt: Alternativen für Komponentenkommunikation
- 6. Umbenannte Lifecycle-Hooks
- 7. Teleport, Fragments und mehrere Root-Elemente
- 8. Weitere Breaking Changes: Attribute, Slots, Reaktivität
- 9. Vue 2 und Vue 3 Breaking Changes im Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum eine Checkliste besser ist als Trial-and-Error
Vue 3 Breaking Changes unterscheiden sich von normalen Deprecation-Warnungen dadurch, dass sie oft ohne Fehlermeldung zur Laufzeit falsch funktionieren, statt beim Build fehlzuschlagen. Ein Filter, der in Vue 2 stillschweigend funktionierte, wird in Vue 3 einfach ignoriert oder als Ausdrucksfehler im Template interpretiert, ohne dass die Konsole eine eindeutige Ursache anzeigt. Genau dieser Unterschied macht Trial-and-Error bei einer Vue-3-Migration ineffizient: Man findet die meisten Breaking Changes erst, wenn ein Feature im Browser sichtbar kaputt ist.
Eine systematische Checkliste der Vue 3 Breaking Changes dreht dieses Vorgehen um. Statt auf sichtbare Symptome zu warten, wird jede bekannte Änderung gezielt im Code gesucht, bevor sie zum Problem wird. Die folgenden Abschnitte decken die Änderungen ab, die in der Praxis am häufigsten übersehen werden: v-model-Syntax, die neue Global API, entfernte Filter, der gestrichene Event Bus und umbenannte Lifecycle-Hooks.
2. v-model: neue Syntax und mehrere Bindungen
Der wohl sichtbarste Punkt unter den Vue 3 Breaking Changes betrifft v-model auf eigenen Komponenten. In Vue 2 band v-model implizit an die Prop value und das Event input. In Vue 3 wird daraus standardmäßig die Prop modelValue und das Event update:modelValue, was auf den ersten Blick wie eine kosmetische Umbenennung wirkt, aber jede Komponente betrifft, die eigenes v-model-Verhalten implementiert. Wer diese Änderung übersieht, bekommt keine Fehlermeldung, sondern schlicht ein v-model, das die Komponente nicht mehr aktualisiert.
Der zweite Teil dieser Änderung ist gleichzeitig eine Verbesserung: Vue 3 erlaubt mehrere v-model-Bindungen auf derselben Komponente mit benannten Argumenten wie v-model:title und v-model:content. Das ersetzt die in Vue 2 übliche Notlösung, zusätzliche zweiseitige Bindungen manuell über props und $emit nachzubauen. Bei der Migration lohnt es sich, gezielt nach Komponenten zu suchen, die mehrere .sync-Modifier verwendeten, da diese jetzt durch mehrfaches v-model ersetzt werden.
// Vue 2: v-model implicitly bound to value prop and input event
export default {
props: ['value'],
emits: ['input'],
methods: {
updateValue(newValue) {
this.$emit('input', newValue)
},
},
}
// <MyInput v-model="text" />
// Vue 3: v-model bound to modelValue prop and update:modelValue event
export default {
props: ['modelValue'],
emits: ['update:modelValue'],
methods: {
updateValue(newValue) {
this.$emit('update:modelValue', newValue)
},
},
}
// Multiple v-model bindings with named arguments - new in Vue 3
// <MyForm v-model:title="title" v-model:content="content" />
3. Global API: aus Vue.use() wird createApp()
Eine der grundlegenderen Vue 3 Breaking Changes betrifft die Global API. In Vue 2 wurde jedes Plugin und jede globale Komponente über den globalen Vue-Import registriert, etwa Vue.use(plugin) oder Vue.component('name', component). Das Problem dabei: Diese Registrierung war global für den gesamten Prozess gültig, was bei Server-Side Rendering mit mehreren gleichzeitigen Anfragen zu geteiltem Zustand zwischen unabhängigen Requests führen konnte.
Vue 3 löst das mit createApp(), das eine isolierte App-Instanz erzeugt. Jede Registrierung, ob Plugin, globale Komponente oder globale Direktive, hängt jetzt an dieser Instanz statt am globalen Vue-Objekt. Für die Migration bedeutet das: Jeder Vue.use()- und Vue.component()-Aufruf muss in einen entsprechenden Aufruf auf dem app-Objekt aus createApp() übersetzt werden, meist gebündelt in der zentralen Einstiegsdatei der Anwendung.
// Vue 2: global registration mutates the shared Vue object
import Vue from 'vue'
import MyPlugin from './my-plugin'
import GlobalButton from './components/GlobalButton.vue'
Vue.use(MyPlugin)
Vue.component('GlobalButton', GlobalButton)
Vue.directive('focus', { inserted(el) { el.focus() } })
new Vue({ render: (h) => h(App) }).$mount('#app')
// Vue 3: createApp() creates an isolated instance, no global mutation
import { createApp } from 'vue'
import MyPlugin from './my-plugin'
import GlobalButton from './components/GlobalButton.vue'
import App from './App.vue'
const app = createApp(App)
app.use(MyPlugin)
app.component('GlobalButton', GlobalButton)
app.directive('focus', { mounted(el) { el.focus() } })
app.mount('#app')
4. Entfernte Filter: Ersatz durch computed und Methoden
Filter waren in Vue 2 eine beliebte Möglichkeit, Werte direkt im Template mit der Pipe-Syntax {{ value | filterName }} zu formatieren. Unter den Vue 3 Breaking Changes gehört die vollständige Entfernung von Filtern zu den Änderungen, die am wenigsten offensichtlich fehlschlagen: Vue 3 interpretiert das Pipe-Zeichen einfach als ungültigen JavaScript-Ausdruck, was zu einer kryptischen Kompilierungswarnung führt, statt klar auf den entfernten Filter hinzuweisen.
Der Vue-Core-Team-Vorschlag als Ersatz: Formatierungslogik wandert entweder in eine computed-Eigenschaft, wenn sie von reaktiven Daten abhängt, oder in eine einfache Methode, wenn sie als reine Funktion mit Argumenten aufgerufen werden kann. Für global genutzte Filter, etwa Währungsformatierung an vielen Stellen im Projekt, bietet sich ein globales Composable oder eine importierte Utility-Funktion an, die überall eingebunden wird, statt einen globalen Filter zu emulieren.
// Vue 2: filter used in template with pipe syntax
// Template: <p>{{ price | currency }}</p>
export default {
filters: {
currency(value) {
return `€${value.toFixed(2)}`
},
},
}
// Vue 3: formatting logic as a plain function, imported where needed
// utils/format.js
export function formatCurrency(value) {
return `€${value.toFixed(2)}`
}
// Component using it directly in the template via a method or computed
import { formatCurrency } from '@/utils/format'
// Template: <p>{{ formatCurrency(price) }}</p>
5. Event Bus entfernt: Alternativen für Komponentenkommunikation
Der globale Event Bus, in Vue 2 meist über eine leere new Vue()-Instanz mit $on, $off und $emit realisiert, ist unter den Vue 3 Breaking Changes vollständig entfernt worden, da $on und $off aus der Instanz-API gestrichen wurden. Das trifft Projekte besonders hart, die den Event Bus als schnelle Lösung für Kommunikation zwischen weit entfernten Komponenten genutzt haben, ohne Props und Events oder einen zentralen Store einzusetzen.
Als direkter Ersatz für einfache Fälle bietet sich ein kleiner, selbst geschriebener Composable auf Basis von reactive() oder externen Bibliotheken wie mitt an, die dieselbe API-Oberfläche mit on, off und emit nachbilden. Für komplexere Kommunikationsmuster ist ein Umzug zu Pinia die nachhaltigere Lösung, weil zentraler Zustand nachvollziehbarer bleibt als lose gekoppelte Events, deren Auslöser im Code schwer zu verfolgen sind.
6. Umbenannte Lifecycle-Hooks
Zwei Lifecycle-Hooks wurden umbenannt, was in der Vue 3 Breaking Changes-Liste leicht übersehen wird, weil beide Varianten weiterhin funktionieren: destroyed heißt jetzt unmounted, und beforeDestroy heißt beforeUnmount. Vue 3 unterstützt aus Kompatibilitätsgründen zwar teilweise beide Namen in der Options API, aber neue Projekte und die Composition-API-Äquivalente onUnmounted() und onBeforeUnmount() verwenden ausschließlich die neue Terminologie.
Der Namenswechsel spiegelt eine begriffliche Klarstellung wider: Eine Vue-Komponente wird nie wirklich zerstört im Sinne von Objektzerstörung, sondern lediglich vom DOM entfernt, also "unmounted". Bei der Migration reicht ein einfacher Suchen-und-Ersetzen-Durchlauf über destroyed und beforeDestroy, um diesen Teil der Vue 3 Breaking Changes abzuhaken, sofern keine Composition API im Spiel ist, wo ohnehin nur die neuen Namen existieren.
7. Teleport, Fragments und mehrere Root-Elemente
Vue 2 erzwang genau ein Root-Element pro Komponenten-Template, was regelmäßig zu unnötigen umschließenden div-Elementen führte, nur um diese Regel einzuhalten. Unter den Vue 3 Breaking Changes ist die Aufhebung dieser Einschränkung eine reine Verbesserung: Fragments erlauben mehrere Root-Elemente in einem Template, wodurch überflüssige Wrapper-Divs entfernt werden können, die oft unerwünschtes CSS-Verhalten wie zusätzliche Flexbox-Kinder erzeugt haben.
Teleport ist eine komplett neue Fähigkeit, die es erlaubt, Teile eines Templates gezielt an eine andere Stelle im DOM zu rendern, etwa direkt an body für Modals und Tooltips, ohne die logische Komponentenhierarchie im Code zu verändern. Vue-2-Projekte lösten dieses Problem meist mit externen Bibliotheken wie portal-vue, deren Funktionalität durch <Teleport to="body"> jetzt direkt im Core abgedeckt ist und bei der Migration ersetzt werden kann.
8. Weitere Breaking Changes: Attribute, Slots, Reaktivität
Neben den großen Änderungen gibt es kleinere Vue 3 Breaking Changes, die im Alltag trotzdem regelmäßig zu Bugs führen. Attribute-Vererbung an Root-Elemente verhält sich bei mehreren Root-Elementen anders als in Vue 2, weshalb explizit definiert werden muss, welches Element $attrs erhält, sofern die Komponente mehr als ein Root-Element hat. $listeners wurde komplett entfernt und in $attrs zusammengeführt, was Event-Handler betrifft, die in Vue 2 separat über v-on="$listeners" weitergereicht wurden.
Bei Slots ändert sich, dass this.$scopedSlots und this.$slots in Vue 2 unterschiedliche Objekte waren, in Vue 3 aber zu einem einheitlichen this.$slots verschmelzen, wobei jeder Slot jetzt konsequent als Funktion statt als VNode-Array behandelt wird. Reaktivität selbst wurde von Object.defineProperty auf ES2015 Proxies umgestellt, wodurch Vue 3 Array-Index-Zuweisungen und das Hinzufügen neuer Properties zu Objekten automatisch erkennt, ohne die in Vue 2 nötigen Vue.set()-Aufrufe.
9. Vue 2 und Vue 3 Breaking Changes im Vergleich
Die folgende Tabelle fasst die wichtigsten Vue 3 Breaking Changes als Checkliste zusammen, sortiert nach der Häufigkeit, mit der sie in echten Migrationsprojekten auftauchen.
| Bereich | Vue 2 | Vue 3 | Fehlverhalten ohne Fix |
|---|---|---|---|
| v-model | value / input | modelValue / update:modelValue | Komponente aktualisiert sich nicht |
| Global API | Vue.use() / Vue.component() | app.use() / app.component() | Plugin oder Komponente fehlt |
| Filter | {{ value | filter }} | computed() oder Methode | Kryptischer Compiler-Fehler |
| Event Bus | new Vue() mit $on/$emit | mitt oder Pinia | $on ist keine Funktion |
| Lifecycle | destroyed / beforeDestroy | unmounted / beforeUnmount | Hook wird nie aufgerufen |
Diese Tabelle eignet sich als Basis für ein Migrations-Ticket pro Zeile: Jede Zeile lässt sich mit einer Grep-Suche im Projekt quantifizieren und als eigener, überschaubarer Arbeitsschritt abschließen, statt alle Vue 3 Breaking Changes gleichzeitig anzugehen.
Mironsoft
Systematische Vue-2-zu-Vue-3-Migrationen
Keine Vue 3 Breaking Changes mehr im Produktivbetrieb übersehen?
Wir prüfen euer Vue-2-Projekt systematisch gegen die vollständige Liste der Vue 3 Breaking Changes und liefern eine priorisierte Migrations-Roadmap statt böser Überraschungen im Live-Betrieb.
Breaking-Change-Audit
Grep-basierte Suche nach v-model, Filtern, Event Bus und Global API
Priorisierte Roadmap
Änderungen nach Risiko und Aufwand sortiert, keine Big-Bang-Migration
Code-Review
Migrationscode gegenprüfen, bevor er in Produktion geht
10. Zusammenfassung
Die wichtigsten Vue 3 Breaking Changes lassen sich in fünf konkrete, gut suchbare Kategorien einteilen: v-model-Syntax auf eigenen Komponenten, der Umbau der Global API zu createApp(), die vollständige Entfernung von Filtern, der gestrichene Event Bus über $on/$off, und umbenannte Lifecycle-Hooks. Jede dieser Änderungen lässt sich gezielt per Grep-Suche im Projekt finden, bevor sie im Browser als kaputtes Feature sichtbar wird.
Der entscheidende Vorteil einer Checkliste gegenüber Trial-and-Error: Migrationen werden planbar, weil jeder Punkt einzeln abgehakt und als eigenständiges Ticket bearbeitet werden kann. Wer zusätzlich die kleineren Änderungen bei Attribute-Vererbung, Slots und Reaktivität im Blick behält, reduziert das Risiko unerwarteter Regressionen bei der Migration auf Vue 3 Breaking Changes erheblich.
Vue 3 Breaking Changes Checkliste — Das Wichtigste auf einen Blick
v-model-Syntax
modelValue statt value, update:modelValue statt input. Mehrere v-model-Bindungen jetzt mit benannten Argumenten möglich.
Global API
createApp() ersetzt globale Vue.use()-Registrierung. Verhindert geteilten Zustand zwischen SSR-Requests.
Filter und Event Bus entfernt
Filter werden durch computed() oder Funktionen ersetzt. Event Bus durch mitt oder Pinia ersetzen.
Lifecycle und Reaktivität
destroyed wird unmounted. Proxy-basierte Reaktivität erkennt neue Properties automatisch, kein Vue.set() mehr nötig.