Async Components & Suspense
Komponenten lazy laden und elegante Ladezustände mit Suspense verwalten.
Warum Async Components?
Nicht jede Komponente wird sofort gebraucht. Modale Dialoge, komplexe Editoren oder Dashboard-Widgets können bei Bedarf geladen werden. Das reduziert die initiale Bundle-Größe und beschleunigt den ersten Seitenaufruf.
Rails-Vergleich: Turbo Frames & Lazy Loading
In Rails nutzt du <turbo-frame src="/comments" loading="lazy">, um Inhalte erst bei Sichtbarkeit nachzuladen. Vue's defineAsyncComponent und <Suspense> lösen dasselbe Problem — nur auf Komponenten-Ebene statt auf HTML-Fragment-Ebene.
defineAsyncComponent
Mit defineAsyncComponent definierst du eine Komponente, die erst geladen wird, wenn sie zum ersten Mal gerendert wird. Vue nutzt dafür dynamische import()-Aufrufe.
<script setup lang="ts">
// Einfach: Nur der Import-Pfad
const HeavyChart = defineAsyncComponent(
() => import('./HeavyChart.vue')
)
</script>
<template>
<!-- Wird erst geladen, wenn die Komponente gerendert wird -->
<HeavyChart v-if="showChart" :data="chartData" />
</template><script setup lang="ts">
const AdminPanel = defineAsyncComponent({
// Die zu ladende Komponente
loader: () => import('./AdminPanel.vue'),
// Wird während des Ladens angezeigt
loadingComponent: LoadingSpinner,
// Verzögerung bevor Loading-Komponente erscheint (ms)
delay: 200,
// Wird bei Fehler angezeigt
errorComponent: ErrorDisplay,
// Timeout — danach gilt es als Fehler (ms)
timeout: 10000,
})
</script>Nuxt Lazy-Prefix
In Nuxt kannst du Komponenten automatisch lazy laden, indem du Lazy vor den Namen setzt: <LazyHeavyChart /> statt <HeavyChart />. Nuxt erledigt den Rest.
Lade- und Fehlerzustände
Die erweiterte Syntax erlaubt es, Platzhalter-Komponenten für den Ladezustand und Fehlerfall zu definieren. So sehen Nutzer immer ein sinnvolles UI.
<script setup lang="ts">
import LoadingSkeleton from './LoadingSkeleton.vue'
import ErrorFallback from './ErrorFallback.vue'
const DashboardWidget = defineAsyncComponent({
loader: () => import('./DashboardWidget.vue'),
loadingComponent: LoadingSkeleton,
errorComponent: ErrorFallback,
delay: 100,
timeout: 5000,
// Optional: bei Fehler eigene Logik
onError(error, retry, fail, attempts) {
if (attempts <= 3) {
// Bis zu 3 Mal automatisch wiederholen
retry()
} else {
fail()
}
},
})
</script>Die Suspense-Komponente
<Suspense> ist Vues eingebaute Lösung, um auf asynchrone Abhängigkeiten zu warten. Sie rendert einen Fallback-Inhalt, bis alle Async-Operationen im #default-Slot abgeschlossen sind.
<template>
<Suspense>
<!-- Hauptinhalt (wartet auf async setup) -->
<template #default>
<AsyncDashboard />
</template>
<!-- Fallback während des Ladens -->
<template #fallback>
<div class="loading">
<Spinner />
<p>Dashboard wird geladen...</p>
</div>
</template>
</Suspense>
</template>
<script setup lang="ts">
// Events von Suspense
// @pending — wenn ein neuer async-Vorgang startet
// @resolve — wenn alle async-Vorgänge abgeschlossen sind
// @fallback — wenn der Fallback angezeigt wird
</script>Wann wird Suspense ausgelöst?
Suspense wartet auf:
- Komponenten mit
async setup() - Top-Level
awaitin<script setup> - Async-Komponenten im Default-Slot
Suspense + Data Fetching
In Nuxt wird <Suspense> oft mit useFetch oder useAsyncData kombiniert. Nuxt wraps Seiten automatisch in Suspense.
<!-- AsyncUserProfile.vue -->
<script setup lang="ts">
// Top-Level await → löst Suspense aus
const { data: user } = await useFetch('/api/users/1')
// Mehrere async-Aufrufe parallel
const [posts, followers] = await Promise.all([
useFetch('/api/users/1/posts'),
useFetch('/api/users/1/followers'),
])
</script>
<template>
<div class="profile">
<h2>{{ user?.name }}</h2>
<p>{{ posts.data.value?.length }} Posts</p>
<p>{{ followers.data.value?.length }} Follower</p>
</div>
</template><template>
<Suspense>
<AsyncUserProfile />
<template #fallback>
<div class="animate-pulse space-y-4">
<div class="h-8 bg-gray-200 rounded w-1/3" />
<div class="h-4 bg-gray-200 rounded w-2/3" />
<div class="h-4 bg-gray-200 rounded w-1/2" />
</div>
</template>
</Suspense>
</template>
<script setup lang="ts">
// Fehler abfangen
onErrorCaptured((error) => {
console.error('Async-Fehler:', error)
// return false → Fehler nicht weiter propagieren
})
</script>Wann Async Components einsetzen?
Nicht jede Komponente sollte lazy geladen werden. Hier ist eine Orientierung:
✅ Gute Kandidaten
- • Modale Dialoge & Drawer
- • Tab-Inhalte (nur aktiver Tab geladen)
- • Schwere Bibliotheken (Charts, Editoren)
- • Admin-Bereiche / seltene Features
- • Below-the-fold Inhalte
❌ Nicht ideal
- • Kleine, häufig genutzte Komponenten
- • Navigation / Header / Footer
- • Komponenten im Above-the-fold Bereich
- • Formular-Inputs und Buttons
- • Alles unter 5 KB
Interaktive Demo
Simuliertes Async-Loading mit konfigurierbarer Verzögerung und Fehlerquote:
Klicke "Laden starten" um die Simulation zu beginnen.
Zusammenfassung
Kernpunkte
defineAsyncComponentlädt Komponenten erst bei Bedarf- In Nuxt reicht der
Lazy-Prefix:<LazyModal /> <Suspense>rendert Fallback-UI während des Ladens- Kombiniere Suspense mit
useFetchfür Data-Fetching - Nur große / seltene Komponenten lazy laden — nicht alles
- Immer Fehler- und Ladezustände definieren