Nuxt 2 zu Nuxt 3 Migration: Praxisleitfaden mit Nuxt Bridge
AI generated
<v/>
{ }
Nuxt · Vue 3 · Migration · Nuxt Bridge
Nuxt 2 zu Nuxt 3 Migration
ein Praxisleitfaden ohne Big-Bang-Rewrite

Eine Nuxt 2 zu Nuxt 3 Migration in einem großen Projekt komplett neu zu schreiben, ist selten realistisch. Mit Nuxt Bridge als Zwischenschritt, einer klaren Reihenfolge bei Vuex, asyncData und Modul-Ablösung lässt sich die Migration in überschaubaren, testbaren Schritten durchführen, statt das Projekt wochenlang lahmzulegen.

22 Min. Lesezeit Nuxt Bridge · Pinia · useAsyncData · Module Nuxt 2.17 · Nuxt 3.x · Vue 3

1. Warum eine Nuxt 2 zu Nuxt 3 Migration keine Kleinigkeit ist

Eine Nuxt 2 zu Nuxt 3 Migration unterscheidet sich fundamental von einem gewöhnlichen Minor-Update. Nuxt 3 basiert auf Vue 3, nutzt mit Nitro eine komplett neue Server-Engine und ersetzt viele der Konzepte, auf denen Nuxt-2-Projekte aufgebaut sind: Vuex weicht Pinia, die Options-API-lastige Struktur weicht der Composition API, und das Modul-Ökosystem musste größtenteils neu geschrieben werden. Wer die Migration unterschätzt und einfach die Paketversion hochsetzt, erlebt spätestens beim ersten Build eine Kaskade von Fehlermeldungen, die kaum Rückschlüsse auf die eigentliche Ursache zulässt.

Der entscheidende Denkfehler bei vielen Teams: Eine Nuxt 2 zu Nuxt 3 Migration wird als reines Technik-Update behandelt, nicht als Projekt mit eigenem Risikoprofil. Größere Nuxt-2-Anwendungen haben über Jahre Module akkumuliert, von denen einige nie ein Nuxt-3-Äquivalent bekommen haben. Genau diese Abhängigkeiten entscheiden am Ende, ob die Migration in Wochen oder Monaten zu schaffen ist. Die folgenden Abschnitte zeigen einen Weg, der auf Nuxt Bridge als Zwischenschritt setzt, statt das Projekt in einem Rutsch neu zu schreiben.

2. Vorbereitung: Bestandsaufnahme vor der Migration

Bevor die erste Zeile Code angefasst wird, braucht eine erfolgreiche Nuxt 2 zu Nuxt 3 Migration eine ehrliche Bestandsaufnahme. Welche Nuxt-Module sind im Einsatz, und existiert für jedes ein gepflegtes Nuxt-3-Äquivalent? Wie stark ist die Codebasis von Vuex durchdrungen, und wie viele Komponenten nutzen noch die Options API mit this.$store? Wie viele Seiten verlassen sich auf asyncData oder die alte fetch-Hook-Variante aus Nuxt 2? Ohne diese Liste lässt sich der Aufwand der Migration nicht realistisch einschätzen, und jede Zeitschätzung bleibt reine Spekulation.

Ein praktikabler Ansatz: Ein Abhängigkeits-Audit mit npm ls kombiniert mit einer Grep-Suche nach this.$store, asyncData( und Vue.extend gibt schnell einen quantitativen Überblick über den Migrationsumfang. Projekte mit mehr als fünfzig Komponenten, die noch auf die Options API mit globalem Store-Zugriff setzen, profitieren stark davon, die Migration in mehrere Phasen zu unterteilen, statt alles gleichzeitig anzufassen. Die Nuxt 2 zu Nuxt 3 Migration gelingt in der Praxis fast immer besser, wenn Bridge, Store und Datenladen getrennt voneinander angegangen werden.


# Audit: which patterns still exist in the Nuxt 2 codebase
grep -rl "this.\$store" src/ | wc -l
grep -rl "asyncData(" pages/ | wc -l
grep -rl "Vue.extend" src/ components/ | wc -l

# List installed Nuxt modules and cross-check for Nuxt 3 equivalents
npm ls --depth=0 | grep "nuxt-"

# Check current Nuxt and Vue versions before starting
npx nuxt --version
npm ls vue

3. Nuxt Bridge als Sicherheitsnetz nutzen

Nuxt Bridge ist das zentrale Werkzeug für eine risikoarme Nuxt 2 zu Nuxt 3 Migration. Es bringt viele Nuxt-3-APIs, darunter Composition API, Nitro-Server-Engine und Vite-Unterstützung, in ein bestehendes Nuxt-2-Projekt zurück, ohne dass sofort auf Nuxt 3 selbst gewechselt werden muss. Das entscheidende daran: Das Projekt bleibt während der gesamten Bridge-Phase lauffähig und deploybar. Teams können Komponente für Komponente auf neue Patterns umstellen, während der Rest der Anwendung unverändert auf Nuxt 2 mit Bridge läuft.

Die Installation erfolgt über das Paket @nuxt/bridge, das die bestehende nuxt-Abhängigkeit im package.json ersetzt. Nach der Installation zeigt der Build-Prozess sofort, welche Module inkompatibel sind, da Bridge intern bereits Nitro statt der alten Nuxt-2-Server-Middleware verwendet. Diese frühe Fehlerrückmeldung ist ein großer Vorteil gegenüber dem direkten Sprung zu Nuxt 3: Inkompatibilitäten werden sichtbar, während die Anwendung noch produktiv läuft, statt erst nach dem vollständigen Umstieg.


# Install Nuxt Bridge instead of jumping straight to Nuxt 3
npm install @nuxt/bridge@npm:@nuxt/bridge-edge -D
npm uninstall nuxt

# nuxt.config.js - enable bridge features incrementally
export default {
  bridge: {
    vite: false,        // enable once build issues are resolved
    nitro: true,        // new server engine, replaces serverMiddleware
    composition: true,  // Composition API available in Options API components
  },
}

# Run dev server with bridge active and watch for compatibility warnings
npm run dev

4. Von nuxt.config.js zu nuxt.config.ts

Ein sichtbarer, aber technisch überschaubarer Teil der Nuxt 2 zu Nuxt 3 Migration ist die Umstellung der Konfigurationsdatei. Nuxt 3 erwartet defineNuxtConfig() statt eines rohen Objekt-Exports, und viele Top-Level-Optionen aus Nuxt 2 wurden umbenannt oder in Untergruppen verschoben. modules bleibt zwar erhalten, aber buildModules aus Nuxt 2 gibt es nicht mehr getrennt, alle Build-Zeit-Module wandern jetzt einheitlich unter modules. env wird durch runtimeConfig ersetzt, was gleichzeitig serverseitige und clientseitige Umgebungsvariablen sauberer trennt.

Für Projekte mit umfangreicher Konfiguration lohnt sich der Umstieg auf TypeScript für die Config-Datei selbst, auch wenn der restliche Code noch JavaScript ist. defineNuxtConfig() liefert vollständige Typinformationen für jede Option, wodurch Tippfehler bei Konfigurationsschlüsseln, die in Nuxt 2 stillschweigend ignoriert wurden, jetzt zur Compile-Zeit auffallen. Das ist gerade bei einer Migration wertvoll, wenn viele Konfigurationswerte gleichzeitig umgeschrieben werden.


// nuxt.config.ts - Nuxt 3 configuration replacing nuxt.config.js
export default defineNuxtConfig({
  modules: [
    '@pinia/nuxt',       // replaces the old Vuex store setup
    '@nuxtjs/tailwindcss',
  ],

  // env (Nuxt 2) is replaced by runtimeConfig with public/private split
  runtimeConfig: {
    apiSecret: process.env.API_SECRET,     // server-side only
    public: {
      apiBase: process.env.API_BASE_URL,   // exposed to the client
    },
  },

  // buildModules from Nuxt 2 merges into modules in Nuxt 3
  nitro: {
    preset: 'node-server',
  },
})

5. Vuex durch Pinia ersetzen

Der Store-Wechsel ist inhaltlich der größte Einzelschritt jeder Nuxt 2 zu Nuxt 3 Migration. Pinia ist der offizielle Nachfolger von Vuex im Nuxt-3-Ökosystem und bringt deutlich weniger Boilerplate mit: keine Mutations mehr, direkte State-Änderungen in Actions, vollständige TypeScript-Inferenz ohne zusätzliche Typdefinitionen. Die Migration eines Vuex-Moduls zu einem Pinia-Store folgt einem klaren Muster: state wird zu einer Funktion, die den Anfangszustand zurückgibt, getters bleiben strukturell fast identisch, und mutations plus actions verschmelzen zu reinen actions-Methoden mit direktem this.property = wert-Zugriff.

In der Praxis migriert man Vuex-Module einzeln, nicht die gesamte Store-Struktur auf einmal. Da Pinia parallel zu Vuex installiert werden kann, lassen sich neue Features direkt in Pinia schreiben, während bestehende Vuex-Module schrittweise übertragen werden. Diese inkrementelle Strategie verhindert, dass die Nuxt 2 zu Nuxt 3 Migration an einem einzigen riesigen Store-Refactoring hängen bleibt, das erst nach Wochen wieder testbar ist.


// stores/cart.js - Pinia store replacing the old Vuex cart module
import { defineStore } from 'pinia'

export const useCartStore = defineStore('cart', {
  state: () => ({
    items: [],
    isLoading: false,
  }),

  getters: {
    // getters stay structurally close to Vuex getters
    itemCount: (state) => state.items.length,
    total: (state) => state.items.reduce((sum, i) => sum + i.price * i.qty, 0),
  },

  actions: {
    // mutations and actions merge into plain methods with direct state writes
    async addItem(product) {
      this.isLoading = true
      try {
        const existing = this.items.find((i) => i.id === product.id)
        if (existing) {
          existing.qty += 1
        } else {
          this.items.push({ ...product, qty: 1 })
        }
      } finally {
        this.isLoading = false
      }
    },
  },
})

6. asyncData und fetch durch useAsyncData und useFetch ersetzen

Das Datenladen ist der zweite große Baustein jeder Nuxt 2 zu Nuxt 3 Migration. Die Options-API-Hooks asyncData() und fetch() aus Nuxt 2 existieren in Nuxt 3 nicht mehr in ihrer alten Form, werden aber durch die Composables useAsyncData() und useFetch() ersetzt, die innerhalb von setup() beziehungsweise script setup aufgerufen werden. Der zentrale Unterschied: Die neuen Composables geben reaktive Refs zurück, keine automatisch auf die Komponente gemergten Properties. Das erzwingt einen expliziteren Umgang mit Ladezuständen, Fehlern und Daten, bringt im Gegenzug aber bessere Typinferenz und einfacheres Testen.

Ein häufiger Migrationsfehler: Entwickler rufen useFetch() in einem onMounted()-Hook auf, weil das dem alten fetch()-Verhalten ähnelt. Das funktioniert, verliert aber den entscheidenden Vorteil, nämlich Server-Side Rendering der geladenen Daten. useFetch() muss auf oberster Ebene von setup() stehen, damit Nuxt den Aufruf während des SSR-Durchlaufs korrekt auflösen und den Zustand für die Hydration serialisieren kann.


// Nuxt 2: Options API with asyncData
export default {
  async asyncData({ $axios, params }) {
    const product = await $axios.$get(`/api/products/${params.id}`)
    return { product }
  },
}

// Nuxt 3: useAsyncData inside script setup - must stay top-level for SSR
const route = useRoute()
const { data: product, pending, error, refresh } = await useAsyncData(
  `product-${route.params.id}`,
  () => $fetch(`/api/products/${route.params.id}`)
)

// useFetch is a thin wrapper for the common REST case
const { data: reviews } = await useFetch(`/api/products/${route.params.id}/reviews`, {
  key: `reviews-${route.params.id}`,
})

7. Auto-Imports und veränderte Verzeichnisstruktur

Nuxt 3 führt Auto-Imports für Composables, Komponenten und Vue-Core-Funktionen wie ref und computed ein, was den Import-Boilerplate massiv reduziert, aber auch eine Umgewöhnung während der Nuxt 2 zu Nuxt 3 Migration erfordert. Dateien aus dem composables/-Verzeichnis stehen automatisch in jeder Komponente zur Verfügung, ohne expliziten Import. Für Migrationsprojekte bedeutet das: Bestehende Utility-Funktionen aus Nuxt 2, die bisher manuell importiert wurden, können nach dem Umzug in composables/ überall im Projekt ohne Import-Zeile genutzt werden.

Die Verzeichnisstruktur selbst hat sich ebenfalls verändert. store/ entfällt zugunsten von Pinia-Stores, die meist unter stores/ abgelegt werden. Das Verzeichnis plugins/ bleibt erhalten, erwartet aber ein anderes Export-Format mit defineNuxtPlugin(). Middleware wandert von middleware/ mit Options-API-Signatur zu Funktionen, die defineNuxtRouteMiddleware() nutzen. Diese strukturellen Änderungen betreffen fast jede Datei im Projekt, weshalb sie sich gut für automatisierte Codemods eignen, statt jede Datei von Hand zu bearbeiten.

8. Modul-Ökosystem: Nuxt-2-Module ablösen oder ersetzen

Die größte Unsicherheit in jeder Nuxt 2 zu Nuxt 3 Migration liegt selten im eigenen Code, sondern in Drittanbieter-Modulen. Beliebte Nuxt-2-Module wie @nuxtjs/auth wurden entweder komplett neu geschrieben, wie im Fall von @sidebase/nuxt-auth, oder still aufgegeben, ohne dass ein offizielles Nuxt-3-Äquivalent existiert. Genau diese Fälle entscheiden am Ende über die Dauer der Migration, weil fehlende Module entweder selbst nachgebaut oder durch grundsätzlich andere Architektur-Entscheidungen ersetzt werden müssen.

Ein pragmatischer Ansatz: Für jedes Modul aus der Bestandsaufnahme wird geprüft, ob ein aktiv gepflegtes Nuxt-3-Modul existiert, ob die Funktionalität mittlerweile Teil des Nuxt-3-Core ist, oder ob ein eigener, kleiner Composable die Abhängigkeit vollständig ersetzen kann. Gerade bei einfachen Modulen, die nur wenige Zeilen Logik gekapselt haben, lohnt sich oft der Ersatz durch einen selbst geschriebenen Composable, statt auf ein möglicherweise unausgereiftes drittes Community-Modul zu setzen.

9. Nuxt 2 und Nuxt 3 im direkten Vergleich

Die folgende Tabelle fasst die wichtigsten Konzeptwechsel zusammen, die in praktisch jeder Nuxt 2 zu Nuxt 3 Migration vorkommen. Sie dient als Checkliste, um keine der zentralen Änderungen im Migrationsplan zu übersehen.

Bereich Nuxt 2 Nuxt 3 Migrationsaufwand
State Management Vuex mit Mutations Pinia mit direkten Actions Mittel bis hoch
Datenladen asyncData / fetch Hook useAsyncData / useFetch Mittel
Konfiguration nuxt.config.js Objekt defineNuxtConfig() mit Typen Gering
Server-Engine connect-basierte Middleware Nitro (server/api/) Hoch
Imports Manuelle Importe überall Auto-Imports für Composables Gering

Die Tabelle macht deutlich, dass der Aufwand nicht gleichmäßig verteilt ist. Die serverseitige Engine und der Store-Wechsel sind die aufwendigsten Teile jeder Nuxt 2 zu Nuxt 3 Migration, während Konfiguration und Imports meist mit überschaubarem Aufwand automatisiert werden können. Wer die Migration plant, sollte diese Reihenfolge in der Zeitschätzung berücksichtigen, statt alle Bereiche gleich zu gewichten.

Mironsoft

Vue- und Nuxt-Migrationen ohne Big-Bang-Risiko

Nuxt 2 zu Nuxt 3 Migration ohne Produktionsausfall?

Wir planen und begleiten eure Nuxt 2 zu Nuxt 3 Migration mit Nuxt Bridge als Zwischenschritt, migrieren Vuex zu Pinia inkrementell und prüfen euer Modul-Ökosystem auf Nuxt-3-Kompatibilität.

Migrations-Audit

Modul-Kompatibilität, Store-Umfang und Datenlade-Patterns systematisch erfassen

Inkrementelle Migration

Nuxt Bridge, Pinia-Umstellung und useAsyncData-Migration Schritt für Schritt

Modul-Ersatz

Fehlende Nuxt-3-Module durch schlanke Composables ersetzen

10. Zusammenfassung

Eine Nuxt 2 zu Nuxt 3 Migration gelingt am zuverlässigsten, wenn sie nicht als einmaliges Rewrite behandelt wird, sondern als Abfolge klar abgegrenzter Phasen. Nuxt Bridge senkt das Risiko, weil das Projekt während der gesamten Übergangszeit lauffähig bleibt und Inkompatibilitäten früh sichtbar werden. Vuex-Module wandern einzeln zu Pinia, asyncData- und fetch-Aufrufe werden Seite für Seite durch useAsyncData und useFetch ersetzt, und das Modul-Audit am Anfang verhindert böse Überraschungen mitten in der Migration.

Der größte Hebel liegt in der Reihenfolge: Konfiguration und Auto-Imports zuerst, weil sie risikoarm und schnell erledigt sind, dann Store-Migration und Datenladen als größere Blöcke, und Modul-Ersatz parallel dazu, sobald klar ist, welche Abhängigkeiten überhaupt betroffen sind. Wer diese Reihenfolge einhält, reduziert das Risiko einer Nuxt 2 zu Nuxt 3 Migration erheblich, ohne das Projekt über Wochen unproduktiv zu machen.

Nuxt 2 zu Nuxt 3 Migration — Das Wichtigste auf einen Blick

Nuxt Bridge zuerst

Bridge bringt Nuxt-3-APIs in ein lauffähiges Nuxt-2-Projekt zurück, ohne sofortigen Vollumstieg. Reduziert das Migrationsrisiko erheblich.

Vuex zu Pinia inkrementell

Module einzeln migrieren, nicht die gesamte Store-Struktur auf einmal. Pinia kann parallel zu Vuex installiert werden.

Datenladen umstellen

asyncData und fetch werden durch useAsyncData und useFetch ersetzt. Aufruf muss top-level in setup() bleiben, sonst kein SSR.

Modul-Audit vorher

Jedes Nuxt-2-Modul auf Nuxt-3-Äquivalent prüfen, bevor die Migration beginnt. Verhindert Überraschungen mitten im Projekt.

11. FAQ: Nuxt 2 zu Nuxt 3 Migration

1Wie lange dauert eine Nuxt 2 zu Nuxt 3 Migration?
Zwischen wenigen Tagen und mehreren Monaten, abhängig von Projektgröße und Modul-Abhängigkeiten. Nuxt Bridge senkt das Risiko, verkürzt aber nicht direkt die Dauer.
2Muss ich Nuxt Bridge zwingend verwenden?
Nein, aber bei mittleren bis großen Projekten deutlich risikoärmer als der direkte Sprung. Inkompatibilitäten werden früh sichtbar, während die Anwendung produktiv läuft.
3Vuex und Pinia parallel nutzen?
Ja, beide gleichzeitig installierbar. Neue Features in Pinia, bestehende Vuex-Module schrittweise migrieren, am Ende Vuex komplett entfernen.
4Was passiert mit asyncData und fetch?
Ersetzt durch useAsyncData und useFetch, aufgerufen in setup() oder script setup. Liefern reaktive Refs statt automatisch gemergter Properties.
5Warum liefert useFetch in onMounted keine SSR-Daten?
useFetch muss top-level in setup() stehen, damit Nuxt den Aufruf während SSR auflöst. In onMounted läuft der Aufruf erst clientseitig.
6Nuxt-2-Module ohne Nuxt-3-Äquivalent?
Erst prüfen ob Funktionalität Teil des Nuxt-3-Core ist. Sonst Alternativmodul suchen oder bei überschaubarer Logik einen eigenen Composable schreiben.
7Ändert sich die Verzeichnisstruktur?
store/ wird zu stores/ mit Pinia, plugins/ bleibt mit defineNuxtPlugin(), middleware/ nutzt defineNuxtRouteMiddleware() statt Options-API-Signaturen.
8Was bringen Auto-Imports?
Composables und Vue-Core-Funktionen stehen automatisch überall zur Verfügung. Utility-Funktionen aus Nuxt 2 profitieren direkt nach Umzug nach composables/.
9nuxt.config.js zu TypeScript umstellen?
Empfehlenswert, auch bei sonst reinem JavaScript-Code. defineNuxtConfig() mit TypeScript zeigt Tippfehler bei Konfigurationsschlüsseln sofort.
10Größter Zeitfresser bei der Migration?
Meist Drittanbieter-Module ohne gepflegtes Nuxt-3-Äquivalent. Ein gründliches Modul-Audit am Anfang verhindert Blockaden mitten im Prozess.