Files
it-know-how/vue/Nuxt Introduction.md
2026-10-28 08:39:04 +01:00

32 KiB
Raw Permalink Blame History

Nuxt – Einführung für Web-Development-Einsteiger

Nuxt ist ein Framework auf Basis von Vue. Es hilft dabei, vollständige Webanwendungen zu bauen, die schnell, suchmaschinenfreundlich und gut strukturiert sind.

Vue stellt vor allem die Bausteine für Benutzeroberflächen bereit. Nuxt ergänzt darum wichtige Konzepte für eine komplette Anwendung: Routing, Server-Side Rendering, Datenabrufe, Layouts, Middleware, Server-Endpunkte und Deployment.

Kurz gesagt: Vue ist der Werkzeugkasten für die Benutzeroberfläche. Nuxt ist ein Anwendungs-Framework, das daraus eine strukturierte Webanwendung macht.


Inhaltsverzeichnis

  1. Voraussetzungen
  2. Was ist Nuxt?
  3. Wie fügt sich Nuxt in den Web-Stack ein?
  4. Wofür benutzt man Nuxt?
  5. Das wichtigste Konzept: Rendering
  6. Ein erstes Nuxt-Projekt
  7. Die Projektstruktur
  8. Zentrale Nuxt-Konzepte
  9. Praxisbeispiel: Produktseite mit API-Aufruf
  10. Praxisbeispiel: Eine eigene Server-API
  11. SEO und Meta-Informationen
  12. Authentifizierung und geschützte Seiten
  13. Typischer Technologie-Stack
  14. Abgrenzung zu verwandten Technologien
  15. Welche Probleme löst Nuxt?
  16. Herausforderungen und typische Fehler
  17. Deployment und Betrieb
  18. Wann ist Nuxt eine gute Wahl?
  19. Zusammenfassung

Voraussetzungen

Für Nuxt sind folgende Grundlagen hilfreich:

  • HTML und CSS
  • JavaScript-Grundlagen
  • TypeScript-Grundlagen, zumindest zum Lesen von Code
  • Vue-Komponenten und Reaktivität
  • grundlegendes Verständnis von HTTP und APIs

Für Vue-Grundlagen siehe 00-Vue Tutorial. Einen größeren Überblick über TypeScript gibt TYPESCRIPT_INTRO. Der vorhandene README - Vue Learning Path beschreibt eine sinnvolle Reihenfolge zum Lernen von Vue.

Man muss nicht bereits professionell mit Vue arbeiten. Es hilft aber, die Grundidee einer Vue-Komponente zu kennen:

<script setup lang="ts">
import { ref } from 'vue'

const message = ref('Hallo Vue!')
</script>

<template>
  <h1>{{ message }}</h1>
</template>

Nuxt verwendet diese Vue-Komponenten weiter. Es nimmt einem zusätzlich viele Entscheidungen rund um die Gesamtstruktur der Anwendung ab.


Was ist Nuxt?

Nuxt ist ein Meta-Framework für Vue. Ein Meta-Framework baut auf einem anderen Framework oder einer Bibliothek auf und ergänzt Funktionen für vollständige Anwendungen.

Bei Nuxt sind die wichtigsten Bestandteile:

Bestandteil Aufgabe
Vue Komponenten, Templates und reaktive Benutzeroberflächen
Nuxt Anwendungsstruktur, Routing, Rendering, Datenabrufe und Konventionen
Vite Entwicklungsserver und Build-Werkzeuge
Nitro Server-Engine für Server-Side Rendering und Server-Endpunkte
TypeScript Statische Typprüfung und bessere IDE-Unterstützung

Ein Nuxt-Projekt kann eine reine Website sein, aber auch eine interaktive Webanwendung mit eigenem Server-Code.

Vue allein und Nuxt im Vergleich

Mit Vue allein baut man eine Benutzeroberfläche. Für eine komplette Anwendung muss man weitere Entscheidungen selbst treffen:

  • Welches Routing wird verwendet?
  • Wie werden Seiten geladen?
  • Wie werden Daten vom Server abgerufen?
  • Wie funktioniert Server-Side Rendering?
  • Wie werden Layouts und Authentifizierung organisiert?
  • Wo liegen Backend-Endpunkte?
  • Wie wird die Anwendung produziert und deployed?

Nuxt liefert für diese Fragen etablierte Lösungen und eine erwartete Projektstruktur.

Das bedeutet nicht, dass Nuxt Vue ersetzt. Nuxt verwendet Vue und erweitert es.


Wie fügt sich Nuxt in den Web-Stack ein?

Eine Webanwendung besteht meistens aus mehreren Schichten:

Benutzer
    ↓
Browser
    ├── HTML
    ├── CSS
    └── JavaScript / Vue
    ↓
Nuxt-Anwendung
    ├── Vue-Komponenten
    ├── Routing
    ├── Rendering
    ├── Datenabrufe
    └── Server-Routen mit Nitro
    ↓
Backend-API / Datenbank / externer Dienst

Der Ablauf einer Anfrage

Bei einer serverseitig gerenderten Seite kann der Ablauf vereinfacht so aussehen:

  1. Ein Benutzer ruft /products/42 im Browser auf.
  2. Der Webserver oder eine Plattform leitet die Anfrage an Nuxt weiter.
  3. Nuxt erkennt anhand des Dateinamens die passende Seite.
  4. Nuxt ruft eventuell Produktdaten von einer API ab.
  5. Nuxt rendert daraus HTML.
  6. Der Browser zeigt dieses HTML sofort an.
  7. Das JavaScript von Vue wird geladen.
  8. Vue übernimmt die Seite und macht sie interaktiv. Dieser Vorgang heißt Hydration.

Bei einer rein clientseitig gerenderten Anwendung würde der Server zunächst ein fast leeres HTML-Dokument liefern. Erst JavaScript baut anschließend den sichtbaren Inhalt auf.


Wofür benutzt man Nuxt?

Nuxt ist besonders interessant, wenn eine Anwendung mehr als eine kleine einzelne Vue-Komponente benötigt.

Typische Use Cases

1. Unternehmenswebsites

Beispiele:

  • Startseite
  • Leistungsübersicht
  • Team- und Kontaktseiten
  • mehrsprachige Inhalte
  • SEO-relevante Landingpages

Server-Side Rendering oder statische Generierung sorgt dafür, dass Inhalte bereits im ausgelieferten HTML vorhanden sind.

2. Blogs und Content-Websites

Nuxt kann Inhalte aus verschiedenen Quellen darstellen:

  • Markdown-Dateien
  • Headless CMS
  • REST-API
  • GraphQL-API
  • Datenbank

Die Seiten können statisch vorgebaut oder bei jeder Anfrage serverseitig erzeugt werden.

3. E-Commerce

Typische Seiten sind:

  • Produktübersicht
  • Produktdetailseite
  • Suche und Filter
  • Warenkorb
  • Checkout
  • Benutzerkonto

Produktseiten profitieren von SEO und schnellem initialem Laden. Interaktive Teile wie Filter oder Warenkorb funktionieren anschließend wie eine normale Vue-Anwendung.

4. Dashboards und interne Anwendungen

Beispiele:

  • Monitoring-Dashboard
  • Verwaltungsoberfläche
  • Reporting-Anwendung
  • internes CRM
  • Projekt- oder Aufgabenverwaltung

Für rein interne Anwendungen ist SEO oft weniger wichtig. Nuxt kann trotzdem nützlich sein, weil Routing, Layouts, Middleware und Datenabrufe gut strukturiert werden.

5. Hybride Webanwendungen

Eine Anwendung kann unterschiedliche Seiten unterschiedlich behandeln:

  • öffentliche Startseite: statisch generiert
  • Blog: statisch generiert oder gecacht
  • Produktseiten: serverseitig gerendert
  • eingeloggtes Dashboard: clientseitig und interaktiv
  • API-Endpunkte: serverseitig

Diese Mischung ist eine der größten Stärken von Nuxt.


Das wichtigste Konzept: Rendering

Rendering bedeutet: Aus Daten und Komponenten wird HTML erzeugt.

Client-Side Rendering (CSR)

Bei Client-Side Rendering liefert der Server zunächst eine JavaScript-Anwendung aus.

Server → HTML-Grundgerüst + JavaScript
Browser → JavaScript startet
Browser → Inhalt wird erzeugt

Vorteile:

  • sehr interaktive Anwendungen
  • einfache mentale Struktur für reine Frontends
  • Navigation kann nach dem ersten Laden schnell sein

Nachteile:

  • initialer Inhalt erscheint eventuell später
  • SEO kann schwieriger sein
  • ohne JavaScript ist wenig Inhalt vorhanden

Server-Side Rendering (SSR)

Bei SSR rendert der Server eine Seite pro Anfrage oder aus einem Cache.

Server → Daten laden und HTML rendern
Browser → fertiges HTML anzeigen
Browser → Vue hydratisiert die Seite

Vorteile:

  • schneller sichtbarer Inhalt
  • gute Grundlage für SEO
  • wichtige Inhalte sind bereits im HTML enthalten

Nachteile:

  • Server muss Rendering leisten
  • Server- und Browser-Code müssen sauber getrennt werden
  • Hydration kann zusätzliche Fehlerquellen erzeugen

Static Site Generation (SSG)

Bei SSG werden Seiten beim Build erzeugt und als statische Dateien ausgeliefert.

Build-Zeit → Seiten erzeugen
Anfrage → fertige HTML-Datei ausliefern

Das passt gut zu:

  • Dokumentationen
  • Blogs
  • Marketing-Websites
  • Seiten mit seltenen Änderungen

Hybrid Rendering

Nuxt kann verschiedene Rendering-Strategien innerhalb eines Projekts kombinieren. Das ist oft praktischer als eine Entscheidung für die gesamte Anwendung.

Beispiel:

/                  → statisch oder gecacht
/blog/**           → statisch
/products/**       → serverseitig gerendert
/account/**        → geschützte interaktive Anwendung
/api/**            → Server-Endpunkte

Die konkrete Konfiguration hängt von Nuxt-Version, Deployment-Ziel und Anwendung ab. Wichtig ist zunächst das Prinzip: Nicht jede Route muss gleich behandelt werden.


Ein erstes Nuxt-Projekt

Ein neues Projekt kann mit dem Nuxt-Scaffolding angelegt werden:

npm create nuxt@latest my-nuxt-app
cd my-nuxt-app
npm install
npm run dev -- -o

Der Entwicklungsserver ist danach normalerweise unter http://localhost:3000 erreichbar.

Produktions-Build

npm run build
npm run preview

Ein Produktions-Build erzeugt eine optimierte Version der Anwendung. Das Ergebnis kann je nach Zielplattform als Node-Anwendung, serverlos oder statisch betrieben werden.

Eine minimale Seite

<!-- pages/index.vue -->
<template>
  <main>
    <h1>Meine erste Nuxt-Seite</h1>
    <p>Diese Seite wird über das dateibasierte Routing gefunden.</p>
  </main>
</template>

Die Datei pages/index.vue entspricht der Route /.


Die Projektstruktur

Eine typische Nuxt-Anwendung sieht ungefähr so aus:

my-nuxt-app/
├── app.vue
├── nuxt.config.ts
├── package.json
├── pages/
│   ├── index.vue
│   ├── about.vue
│   └── products/
│       └── [id].vue
├── components/
│   ├── AppHeader.vue
│   └── ProductCard.vue
├── composables/
│   └── useProducts.ts
├── layouts/
│   └── default.vue
├── middleware/
│   └── auth.ts
├── plugins/
├── server/
│   └── api/
│       └── products.get.ts
├── public/
├── assets/
└── types/

Wichtige Verzeichnisse und Dateien

Pfad Zweck
app.vue globaler Einstiegspunkt der Anwendung
pages/ Seiten und automatisch erzeugte Routen
components/ wiederverwendbare Vue-Komponenten
composables/ wiederverwendbare Vue-Logik
layouts/ gemeinsame Seitenrahmen
middleware/ Logik vor dem Aufruf einer Route
plugins/ Erweiterungen, die beim Start registriert werden
server/api/ eigene API-Endpunkte
public/ Dateien mit unverändert erreichbaren URLs
assets/ zu verarbeitende Assets wie Stylesheets
nuxt.config.ts Konfiguration von Nuxt
package.json Abhängigkeiten und Skripte

Dateibasiertes Routing

Der Dateiname bestimmt die URL:

pages/index.vue             → /
pages/about.vue             → /about
pages/products/index.vue    → /products
pages/products/[id].vue     → /products/:id

Ein dynamisches Segment wird in eckigen Klammern geschrieben:

<!-- pages/products/[id].vue -->
<script setup lang="ts">
const route = useRoute()

const productId = route.params.id
</script>

<template>
  <h1>Produkt {{ productId }}</h1>
</template>

Nuxt verwendet dafür Vue Router, konfiguriert aber die üblichen Fälle automatisch.


Zentrale Nuxt-Konzepte

Komponenten

Komponenten in components/ können in der Regel automatisch verwendet werden:

<!-- components/ProductCard.vue -->
<script setup lang="ts">
defineProps<{
  name: string
  price: number
}>()
</script>

<template>
  <article>
    <h2>{{ name }}</h2>
    <p>{{ price.toFixed(2) }} €</p>
  </article>
</template>

Verwendung:

<template>
  <ProductCard name="Tastatur" :price="79.90" />
</template>

Die automatische Registrierung reduziert Import-Code. Bei großen Projekten sollte man trotzdem sinnvolle Namen und klare Verzeichnisstrukturen verwenden.

Layouts

Ein Layout enthält wiederkehrende Struktur, zum Beispiel Navigation und Footer:

<!-- layouts/default.vue -->
<template>
  <div>
    <header>
      <NuxtLink to="/">Shop</NuxtLink>
      <NuxtLink to="/products">Produkte</NuxtLink>
    </header>

    <main>
      <slot />
    </main>

    <footer>© 2026 Mein Shop</footer>
  </div>
</template>

Eine Seite kann ein bestimmtes Layout verwenden:

<script setup lang="ts">
definePageMeta({
  layout: 'admin',
})
</script>

Navigation

Für interne Links wird normalerweise NuxtLink verwendet:

<NuxtLink to="/about">Über uns</NuxtLink>

Bei der Navigation kann Nuxt Seiten vorladen oder clientseitig wechseln, ohne das gesamte Dokument neu zu laden.

Composables

Ein Composable kapselt wiederverwendbare Logik:

// composables/useCounter.ts
export function useCounter() {
  const count = ref(0)

  function increment() {
    count.value += 1
  }

  return {
    count,
    increment,
  }
}

Verwendung:

<script setup lang="ts">
const { count, increment } = useCounter()
</script>

<template>
  <button @click="increment">
    {{ count }}
  </button>
</template>

Composables sind vergleichbar mit wiederverwendbaren Hilfsobjekten oder Funktionen. Sie helfen, Seiten schlank zu halten.

useFetch und $fetch

Nuxt bietet Hilfen für API-Aufrufe.

<script setup lang="ts">
const { data, pending, error } = await useFetch('/api/products')
</script>

useFetch ist besonders für Daten geeignet, die Teil des initialen Seitenaufbaus sind. Nuxt kann den Abruf bei SSR berücksichtigen und die Daten an den Browser weitergeben.

$fetch wird häufig für einzelne Aktionen verwendet:

async function deleteProduct(id: string) {
  await $fetch(`/api/products/${id}`, {
    method: 'DELETE',
  })
}

Als grobe Orientierung:

Werkzeug Typischer Zweck
useFetch Daten für eine Seite laden, inklusive Lade- und Fehlerzustand
useAsyncData komplexere oder individuellere asynchrone Datenlogik
$fetch einzelne Requests, Formulare und Benutzeraktionen

Middleware

Middleware kann vor dem Anzeigen einer Seite ausgeführt werden:

// middleware/auth.ts
export default defineNuxtRouteMiddleware(() => {
  const user = useCurrentUser()

  if (!user.value) {
    return navigateTo('/login')
  }
})

Eine Seite kann diese Middleware aktivieren:

<script setup lang="ts">
definePageMeta({
  middleware: 'auth',
})
</script>

Wichtig: Client-seitige Middleware allein schützt keine geheimen Daten. Die Berechtigung muss zusätzlich auf dem Server oder in der Backend-API geprüft werden.

Runtime-Konfiguration

Konfigurationswerte gehören nicht hartcodiert in Komponenten:

// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    apiSecret: '',
    public: {
      apiBase: '/api',
    },
  },
})

Verwendung:

const config = useRuntimeConfig()

// Darf im Browser verwendet werden:
console.log(config.public.apiBase)

// Nur serverseitig verfügbar:
console.log(config.apiSecret)

Ein Secret darf nicht unter public abgelegt werden. Alles unter runtimeConfig.public kann grundsätzlich im Browser landen.


Praxisbeispiel: Produktseite mit API-Aufruf

Angenommen, eine Produktdetailseite soll Daten laden und verschiedene Zustände anzeigen.

Dynamische Seite

<!-- pages/products/[id].vue -->
<script setup lang="ts">
type Product = {
  id: string
  name: string
  description: string
  price: number
}

const route = useRoute()

const { data: product, pending, error } = await useFetch<Product>(
  `/api/products/${route.params.id}`,
)
</script>

<template>
  <main>
    <p v-if="pending">Produkt wird geladen …</p>

    <p v-else-if="error">
      Das Produkt konnte nicht geladen werden.
    </p>

    <article v-else-if="product">
      <h1>{{ product.name }}</h1>
      <p>{{ product.description }}</p>
      <strong>{{ product.price.toFixed(2) }} €</strong>
    </article>
  </main>
</template>

Warum ist useFetch hier sinnvoll?

Bei SSR kann Nuxt den Datenabruf während des Renderns durchführen. Der Benutzer erhält dadurch direkt eine Seite mit Produktinhalt. Der Browser muss nicht erst eine leere Seite anzeigen und anschließend die Daten nachladen.

Außerdem werden pending, error und data als reaktive Zustände bereitgestellt.

API-Endpunkt dazu

// server/api/products/[id].get.ts
type Product = {
  id: string
  name: string
  description: string
  price: number
}

export default defineEventHandler((event): Product => {
  const id = getRouterParam(event, 'id')

  return {
    id: id ?? 'unknown',
    name: 'Beispielprodukt',
    description: 'Ein Produkt aus einer Nuxt-Server-Route.',
    price: 49.90,
  }
})

In einer echten Anwendung würde der Handler beispielsweise eine Datenbank oder ein externes Backend aufrufen. Die Beispieldaten dienen nur dazu, den Ablauf zu zeigen.


Praxisbeispiel: Eine eigene Server-API

Nuxt verwendet Nitro als Server-Engine. Dateien in server/api/ werden zu API-Endpunkten.

// server/api/hello.get.ts
export default defineEventHandler(() => {
  return {
    message: 'Hallo aus einer Nuxt-Server-Route',
  }
})

Der Endpunkt ist unter /api/hello erreichbar.

Eine POST-Route kann den Request-Body lesen:

// server/api/contact.post.ts
export default defineEventHandler(async (event) => {
  const body = await readBody<{
    name?: string
    message?: string
  }>(event)

  if (!body.name || !body.message) {
    throw createError({
      statusCode: 400,
      statusMessage: 'Name und Nachricht sind erforderlich.',
    })
  }

  return {
    accepted: true,
    name: body.name,
  }
})

Aufruf aus einer Komponente:

await $fetch('/api/contact', {
  method: 'POST',
  body: {
    name: 'Ada Lovelace',
    message: 'Hallo!',
  },
})

Ist Nuxt damit ein vollständiges Backend?

Nuxt kann serverseitige Logik bereitstellen, ist aber nicht automatisch ein Ersatz für jedes Backend. Für kleinere Anwendungen oder Backend-for-Frontend-Endpunkte ist es sehr praktisch.

Ein umfangreiches Backend mit komplexen Geschäftsregeln, vielen Integrationen und großen Datenmengen kann weiterhin als separater Dienst betrieben werden, zum Beispiel mit FastAPI, Django, Go oder einem anderen Backend-Framework.


SEO und Meta-Informationen

Eine öffentlich erreichbare Seite sollte einen passenden Titel und eine Beschreibung haben:

<script setup lang="ts">
useSeoMeta({
  title: 'Tastaturen – Mein Shop',
  description: 'Mechanische und ergonomische Tastaturen entdecken.',
})
</script>

Für dynamische Seiten können die Werte aus den geladenen Daten entstehen:

<script setup lang="ts">
const { data: product } = await useFetch<Product>('/api/products/42')

useSeoMeta({
  title: () => product.value ? `${product.value.name} – Mein Shop` : 'Produkt',
  description: () => product.value?.description,
})
</script>

SSR oder SSG sorgt dafür, dass Suchmaschinen wichtige Inhalte leichter vorfinden. Das allein garantiert allerdings keine gute Platzierung. Zusätzlich sind unter anderem relevant:

  • sinnvolle Inhalte
  • verständliche URLs
  • gute Ladezeiten
  • semantisches HTML
  • mobile Nutzbarkeit
  • interne Verlinkung
  • korrekte Canonical- und Open-Graph-Metadaten

Authentifizierung und geschützte Seiten

Eine typische Anwendung unterscheidet zwischen öffentlichen und geschützten Bereichen:

/                  → öffentlich
/products          → öffentlich
/login             → öffentlich
/account           → Anmeldung erforderlich
/admin             → Anmeldung und Admin-Rolle erforderlich

Ein typischer Ablauf besteht aus mehreren Schichten:

  1. Frontend zeigt Login-Formular.
  2. Formular ruft eine Server-Route oder externe API auf.
  3. Server erstellt eine Session oder ein Token.
  4. Browser erhält beispielsweise ein sicheres HttpOnly-Cookie.
  5. Route-Middleware verbessert die Benutzerführung.
  6. Jeder geschützte Server-Endpunkt prüft die Session erneut.

Der wichtige Sicherheitsgrundsatz lautet:

Frontend-Prüfungen sind Komfortfunktionen. Autorisierung muss serverseitig durchgesetzt werden.

Ein Benutzer kann JavaScript im Browser verändern oder API-Aufrufe direkt senden. Deshalb darf ein Server nicht darauf vertrauen, dass eine Nuxt-Seite eine Route korrekt versteckt.


Typischer Technologie-Stack

Ein möglicher Stack für eine Nuxt-Anwendung sieht so aus:

Schicht Beispiel Aufgabe
Sprache TypeScript Typen, bessere Wartbarkeit und IDE-Unterstützung
UI Vue 3 Komponenten und Reaktivität
Framework Nuxt Routing, Rendering, Konventionen und Anwendungsschicht
Build Vite Entwicklungsserver und Bundling
Server Nitro SSR und Server-Endpunkte
Styling CSS, Tailwind CSS oder UI-Bibliothek Darstellung
Datenzugriff useFetch, $fetch, REST oder GraphQL Kommunikation mit APIs
Backend Nitro-Route oder separater Dienst Geschäftslogik und Integrationen
Datenbank PostgreSQL, MySQL, SQLite usw. dauerhafte Datenhaltung
Authentifizierung Session- oder OAuth-Lösung Benutzer und Berechtigungen
Tests Vitest, Playwright Unit-, Integrations- und End-to-End-Tests
Betrieb Node, Docker, Serverless oder Plattform Auslieferung der Anwendung

Nuxt schreibt Datenbank und Backend nicht zwingend vor. Es ist möglich, Nuxt mit einer externen API zu verbinden oder die Server-Seite teilweise innerhalb derselben Anwendung zu betreiben.

Beispiel für einen Stack

Browser
  ↓
Nuxt + Vue + TypeScript
  ↓
Nitro-Server-Routen
  ↓
PostgreSQL

Oder bei einem getrennten Backend:

Browser
  ↓
Nuxt + Vue + TypeScript
  ↓
REST- oder GraphQL-API
  ↓
FastAPI / Django / Node / Go
  ↓
PostgreSQL

Abgrenzung zu verwandten Technologien

Nuxt und Vue

Vue Nuxt
UI-Framework Meta-Framework auf Basis von Vue
Komponenten und Reaktivität komplette Anwendungsstruktur
Routing muss ergänzt werden dateibasiertes Routing ist integriert
SSR muss eingerichtet werden SSR, SSG und Hybrid Rendering sind vorgesehen
freie Architektur stärkere Konventionen

Wenn nur eine kleine interaktive Komponente oder ein einzelnes Dashboard benötigt wird, reicht Vue möglicherweise aus. Für eine größere Website mit mehreren Seiten ist Nuxt oft bequemer.

Nuxt und Vue mit Vite

Vue mit Vite ist ein frei zusammengestelltes Frontend-Projekt. Man entscheidet selbst über:

  • Router
  • Datenabrufe
  • State Management
  • SSR
  • Projektstruktur
  • Deployment

Das bietet maximale Kontrolle, führt aber auch zu mehr Architekturentscheidungen. Nuxt nimmt davon einiges ab.

Nuxt und React

React ist eine UI-Bibliothek bzw. das Zentrum eines großen Ökosystems. Nuxt ist auf Vue spezialisiert.

Ein direkter Vergleich ist häufig:

Vue + Nuxt      ↔      React + Next.js

Beide Ansätze können SSR, SSG, Routing und Server-Code bereitstellen. Die Unterschiede liegen vor allem in:

  • Syntax und Komponentenmodell
  • Reaktivitätsmodell
  • Ökosystem
  • Team-Erfahrung
  • vorhandenen Bibliotheken

Nuxt und Angular

Angular ist ein umfassendes Frontend-Framework mit vielen festen Konzepten, beispielsweise Dependency Injection, Services und RxJS.

Nuxt/Vue wirkt oft schrittweise und komponentenorientiert. Angular kann für sehr große, stark standardisierte Enterprise-Teams passend sein; Nuxt ist häufig leichter zugänglich, insbesondere wenn Vue bereits bekannt ist.

Nuxt und Astro

Astro ist besonders stark bei inhaltsorientierten Websites. Es liefert standardmäßig möglichst wenig JavaScript aus und kann interaktive Komponenten als sogenannte Islands nachladen.

Nuxt ist stärker, wenn die Anwendung selbst eine große interaktive Vue-Anwendung ist.

Grobe Orientierung:

Content zuerst, wenig Interaktion → Astro kann gut passen
Interaktive Vue-Anwendung        → Nuxt kann gut passen

Nuxt und klassische Backend-Frameworks

Frameworks wie Django, FastAPI, Rails oder Spring konzentrieren sich primär auf serverseitige Geschäftslogik, APIs und Datenverarbeitung.

Nuxt konzentriert sich auf die Webanwendung und die Verbindung aus UI, Rendering und Server-Schicht. Eine Nuxt-Anwendung kann ein separates Backend konsumieren oder kleinere Server-Funktionen selbst übernehmen.


Welche Probleme löst Nuxt?

1. Routing

Ohne Konventionen muss man Routen oft manuell registrieren. Nuxt leitet Routen aus pages/ ab.

2. SEO bei dynamischen Anwendungen

SSR und SSG ermöglichen HTML mit echtem Inhalt, statt nur ein leeres JavaScript-Grundgerüst auszuliefern.

3. Gemeinsame Projektstruktur

Teammitglieder wissen, wo Seiten, Komponenten, Middleware und Server-Routen liegen.

4. Datenabrufe

useFetch und useAsyncData unterstützen typische Abläufe mit Loading-, Fehler- und SSR-Zuständen.

5. Wiederverwendbare Layouts

Header, Navigation und Footer müssen nicht in jede Seite kopiert werden.

6. Server- und Client-Integration

Server-Routen können in derselben Codebasis wie die Vue-Seiten liegen. Das ist besonders nützlich für kleine APIs oder Backend-for-Frontend-Schichten.

7. Code-Splitting und Build

Nuxt teilt Seiten und Abhängigkeiten für die Auslieferung auf und bringt passende Build-Werkzeuge mit.

8. Deployment

Nuxt kann je nach Konfiguration als Node-Anwendung, statisch oder auf serverlosen Plattformen betrieben werden.


Herausforderungen und typische Fehler

Server und Browser sind nicht dasselbe

Beim SSR läuft Code zunächst auf dem Server. Browser-APIs wie window, document oder localStorage existieren dort nicht.

Problematisches Beispiel:

// Wird beim SSR fehlschlagen, wenn window nicht existiert.
const width = window.innerWidth

Eine mögliche clientseitige Lösung:

const width = ref<number | null>(null)

onMounted(() => {
  width.value = window.innerWidth
})

Oder man verwendet import.meta.client, wenn die Logik gezielt nur im Browser ausgeführt werden soll.

Hydration-Mismatches

Der Server rendert HTML. Anschließend erwartet Vue im Browser grundsätzlich dieselbe Struktur. Wenn Server und Browser unterschiedliche Werte erzeugen, kann eine Warnung oder ein fehlerhaftes Verhalten entstehen.

Riskant ist beispielsweise ein zufälliger Wert direkt im Template:

<template>
  <p>{{ Math.random() }}</p>
</template>

Der Server und der Browser erzeugen unterschiedliche Zahlen. Besser ist ein kontrollierter, stabiler Wert oder eine bewusst erst im Browser erzeugte Anzeige.

Datenabrufe doppelt verstehen

Nicht jeder API-Aufruf sollte mit useFetch umgesetzt werden. Für initiale Seitendaten ist useFetch meist passend, für eine Aktion nach einem Button-Klick häufig $fetch.

Secrets im Browser

Private Schlüssel und Passwörter gehören nicht in Komponenten und nicht in runtimeConfig.public. Browser-Code ist für den Benutzer sichtbar.

Server-Routen sind keine automatische Geschäftslogik

Eine Datei unter server/api/ ist zunächst nur ein Endpunkt. Validierung, Fehlerbehandlung, Berechtigungsprüfung, Logging und Datenbanktransaktionen müssen trotzdem sorgfältig implementiert werden.

Caching und veraltete Daten

SSR, statische Generierung und Caching können zu sehr schnellen Seiten führen. Gleichzeitig muss geklärt werden:

  • Wie lange darf eine Antwort gecacht werden?
  • Wann muss eine Seite neu erzeugt werden?
  • Wie werden Änderungen sichtbar?
  • Welche Daten sind benutzerspezifisch und dürfen niemals gemeinsam gecacht werden?

Besonders bei Login-Daten und persönlichen Inhalten darf kein versehentliches Server-Caching stattfinden.

Nuxt-Konventionen lernen

Nuxt reduziert eigenen Architekturcode, führt aber eigene Konzepte ein. Am Anfang müssen unter anderem folgende Fragen beantwortet werden:

  • Wann verwende ich useFetch?
  • Wann läuft Code auf dem Server?
  • Wie funktionieren Layouts und Middleware?
  • Wo gehört ein Plugin hin?
  • Wann brauche ich eine Server-Route?

Diese Lernkurve ist der Preis für die produktive Struktur, die Nuxt bereitstellt.


Deployment und Betrieb

Nuxt wird normalerweise zunächst gebaut:

npm run build

Danach wird das erzeugte Ergebnis entsprechend der Zielumgebung gestartet. Bei einem Node-Deployment ist das Prinzip typischerweise:

node .output/server/index.mjs

Die konkrete Bereitstellung kann beispielsweise erfolgen über:

  • eigenen Linux-Server
  • Docker
  • Kubernetes
  • Plattformen mit Node-Unterstützung
  • serverlose Plattformen
  • statisches Hosting bei einer vollständig statisch generierten Website

Betriebspunkte

Für eine produktive Anwendung sollten unter anderem berücksichtigt werden:

  • Umgebungsvariablen
  • HTTPS
  • Logging
  • Monitoring
  • Fehlerseiten
  • Performance und Caching
  • Datenbankverbindungen
  • Rate Limiting bei APIs
  • Sicherheitsupdates der Abhängigkeiten
  • Build- und Deployment-Pipeline

Nuxt vereinfacht den Build, nimmt aber nicht die Verantwortung für einen sicheren und stabilen Betrieb ab.


Wann ist Nuxt eine gute Wahl?

Nuxt ist häufig eine gute Wahl, wenn:

  • Vue bereits verwendet wird oder im Team bekannt ist
  • mehrere Seiten und ein echtes Routing benötigt werden
  • SEO und initiale Ladezeit wichtig sind
  • SSR, SSG oder hybride Strategien gebraucht werden
  • Frontend und kleinere Server-Funktionen zusammengehören
  • eine konventionelle, gut erkennbare Projektstruktur hilfreich ist
  • eine interaktive Webanwendung entstehen soll

Wann reicht Vue mit Vite?

Vue mit Vite kann ausreichen, wenn:

  • die Anwendung nur aus einem kleinen interaktiven Bereich besteht
  • SSR nicht erforderlich ist
  • SEO keine Rolle spielt
  • die Architektur vollständig selbst festgelegt werden soll
  • bereits ein separates Frontend-Setup vorhanden ist

Wann ist ein separates Backend sinnvoll?

Ein eigenes Backend ist oft sinnvoll, wenn:

  • komplexe Geschäftslogik zentral für mehrere Clients benötigt wird
  • neben der Website auch mobile Apps auf dieselbe API zugreifen
  • umfangreiche Hintergrundverarbeitung erforderlich ist
  • Datenbankzugriffe und Berechtigungen zentral gebündelt werden sollen
  • ein vorhandenes Backend weiterverwendet wird

Nuxt kann in diesem Fall weiterhin das Frontend und gegebenenfalls eine dünne Backend-for-Frontend-Schicht bereitstellen.


Zusammenfassung

Nuxt ist ein Meta-Framework für Vue. Es verbindet Vue-Komponenten mit den Funktionen, die man beim Bau einer vollständigen Webanwendung benötigt:

  • dateibasiertes Routing
  • Layouts und Middleware
  • Server-Side Rendering
  • Static Site Generation
  • hybrides Rendering
  • Datenabrufe
  • SEO-Unterstützung
  • Server-API-Routen
  • Build und Deployment

Der typische Denkansatz ist:

Vue       → Wie baue ich die Benutzeroberfläche?
Nuxt      → Wie baue und betreibe ich die gesamte Webanwendung?
Nitro     → Wie läuft die Server-Seite?
Vite      → Wie entwickle und baue ich das Projekt?
TypeScript→ Wie mache ich den Code sicherer und wartbarer?

Nuxt löst viele wiederkehrende Architekturprobleme, bringt dafür aber eigene Konzepte und eine Server-/Client-Komplexität mit. Wer Vue-Grundlagen beherrscht und versteht, wo Code ausgeführt wird, kann mit Nuxt sowohl klassische Websites als auch umfangreiche interaktive Anwendungen entwickeln.

Empfohlener Lernweg

  1. JavaScript- und TypeScript-Grundlagen lernen.
  2. Vue-Komponenten, Templates und Reaktivität verstehen.
  3. Eine kleine Vue-Anwendung bauen.
  4. Ein Nuxt-Projekt mit pages/ und components/ erstellen.
  5. Navigation und dynamische Routen ergänzen.
  6. Daten mit useFetch laden.
  7. Eine eigene server/api/-Route schreiben.
  8. SSR, SSG und Hydration vergleichen.
  9. Authentifizierung und serverseitige Berechtigungen ergänzen.
  10. Die Anwendung bauen, deployen und beobachten.

Der wichtigste praktische Lernschritt ist, eine kleine Anwendung vollständig umzusetzen: zum Beispiel eine Produktliste mit Detailseiten, Loading- und Fehlerzuständen, einer Server-Route und einer geschützten Admin-Seite.