Wann welches Composable die richtige Wahl für den Datenabruf ist
useFetch ist im Kern ein Convenience-Wrapper um useAsyncData in Kombination mit $fetch. Sobald eigene Transform-Funktionen, präzise Caching-Keys oder eine komplexere Datenquelle jenseits eines einfachen HTTP-Requests ins Spiel kommen, ist der direkte Griff zu useAsyncData meist die klarere Wahl.
Inhaltsverzeichnis
- 1. Das Grundproblem: Daten laden in einer SSR-Anwendung
- 2. useFetch als Convenience-Wrapper
- 3. useAsyncData mit eigener Transform-Funktion
- 4. Caching-Keys im Detail
- 5. Entscheidungskriterien: Wann welches Composable?
- 6. Häufige Fallstricke bei doppeltem Datenabruf
- 7. Verhalten zwischen Server und Client
- 8. Die watch-Option: Auf reaktive Parameter reagieren
- 9. Fazit: Zwei Werkzeuge für denselben Zweck
- 10. Zusammenfassung
- 11. FAQ
1. Das Grundproblem: Daten laden in einer SSR-Anwendung
In einer serverseitig gerenderten Nuxt-Anwendung muss ein Datenabruf sowohl auf dem Server als auch im Browser korrekt funktionieren, ohne dass die Daten doppelt geladen werden. Würde man einfach ein normales fetch in einem onMounted-Hook aufrufen, gäbe es beim initialen Rendern keine Daten auf dem Server, und der Nutzer sähe kurzzeitig einen leeren Zustand, bevor der Browser die Daten nachlädt.
Nuxt löst dieses Problem durch spezielle Daten-Composables, die den Zustand eines asynchronen Abrufs automatisch zwischen Server und Client synchronisieren. Der Server führt den Abruf aus, serialisiert das Ergebnis in den initialen HTML-Payload, und der Client übernimmt dieses Ergebnis bei der Hydration, ohne den Request ein zweites Mal auszuführen. Genau hier setzen sowohl useFetch als auch useAsyncData an.
2. useFetch als Convenience-Wrapper
useFetch(url, options) kombiniert intern useAsyncData mit $fetch und generiert dabei automatisch einen sinnvollen Caching-Key aus der URL und den übergebenen Optionen. Für den häufigsten Fall, nämlich das direkte Laden von Daten von einem bekannten Endpunkt, spart das Boilerplate-Code, da weder ein eigener Key noch eine eigene Handler-Funktion geschrieben werden muss.
Die Rückgabewerte von useFetch entsprechen denen von useAsyncData: data, pending, error und refresh stehen als reaktive Werte zur Verfügung. Zusätzlich unterstützt useFetch dieselben Optionen wie $fetch direkt, etwa method, query oder headers, was die Definition eines typischen GET- oder POST-Requests sehr kompakt macht.
3. useAsyncData mit eigener Transform-Funktion
useAsyncData(key, handler, options) ist das flexiblere, allgemeinere der beiden Composables. Der handler ist eine beliebige asynchrone Funktion, die nicht zwingend einen HTTP-Request ausführen muss, sie kann auch mehrere Requests kombinieren, Daten aus einer Datenbank lesen oder eine komplexe Berechnung durchführen. Der key wird explizit angegeben und steuert, unter welchem Bezeichner das Ergebnis im Nuxt-Payload zwischengespeichert wird.
Besonders nützlich ist die transform-Option, mit der sich die Rohantwort direkt nach dem Abruf in eine passendere Form bringen lässt, etwa um nur benötigte Felder herauszufiltern oder mehrere Endpunkte zu einem gemeinsamen Ergebnisobjekt zusammenzuführen. Dadurch bleibt das gespeicherte Payload kleiner und die Komponente arbeitet direkt mit der gewünschten Datenstruktur.
// useFetch: kompakt für den Standardfall
const { data: product, pending, error } = await useFetch(
`/api/products/${id}`,
{ key: `product-${id}` }
);
// useAsyncData: volle Kontrolle über Handler und Transform
const { data: summary } = await useAsyncData(
`product-summary-${id}`,
async () => {
const [product, reviews] = await Promise.all([
$fetch(`/api/products/${id}`),
$fetch(`/api/products/${id}/reviews`),
]);
return { product, averageRating: reviews.average };
},
{
transform: (data) => ({
title: data.product.title,
rating: data.averageRating,
}),
}
);
4. Caching-Keys im Detail
Der Caching-Key bestimmt, unter welchem Eintrag im Nuxt-Payload ein Ergebnis abgelegt wird, und er entscheidet damit auch darüber, ob zwei Aufrufe an derselben Stelle im Code dasselbe zwischengespeicherte Ergebnis teilen. Bei useFetch wird der Key automatisch aus URL, Methode und Body generiert, was in den meisten Fällen ausreichend eindeutig ist, aber bei dynamisch generierten URLs mit komplexen Query-Parametern nicht immer optimal funktioniert.
Bei useAsyncData muss der Key explizit vergeben werden, was zunächst mehr Schreibarbeit bedeutet, aber auch präzise Kontrolle ermöglicht. Ein häufiges Muster ist, den Key aus einer Kombination von Routennamen und relevanten Parametern zusammenzusetzen, etwa `product-${id}-${locale}`, um sicherzustellen, dass unterschiedliche Sprachversionen oder Varianten nicht versehentlich denselben zwischengespeicherten Wert teilen.
5. Entscheidungskriterien: Wann welches Composable?
Für einen einfachen, direkten Abruf von einem einzelnen REST-Endpunkt, bei dem die Rohantwort direkt verwendet werden kann, ist useFetch in aller Regel die kürzere und lesbarere Wahl. Der automatisch generierte Key und die direkte Übernahme der $fetch-Optionen reduzieren den Code auf das Wesentliche, ohne dass dabei Funktionalität verloren geht.
Sobald mehrere Requests kombiniert werden müssen, eine eigene Transform-Logik nötig ist, die Datenquelle kein einfacher HTTP-Aufruf ist, oder der Caching-Key aus gutem Grund manuell kontrolliert werden soll, ist useAsyncData die passendere Wahl. Als Faustregel gilt: useFetch für den einfachen Fall, useAsyncData für alles, was darüber hinausgeht.
6. Häufige Fallstricke bei doppeltem Datenabruf
Ein sehr häufiger Fehler besteht darin, Daten zusätzlich zu useFetch oder useAsyncData noch einmal in einem onMounted-Hook per direktem $fetch-Aufruf zu laden. Das führt dazu, dass der Request tatsächlich zweimal ausgeführt wird, einmal serverseitig über das Composable und einmal zusätzlich im Browser, was unnötige Serverlast und ein kurzzeitig inkonsistentes UI erzeugt.
Ein zweiter häufiger Fallstrick betrifft fehlende oder falsch gesetzte Keys bei dynamischen Routen: Verwendet man denselben, statischen Key für eine Komponente, die auf verschiedenen Detailseiten mit unterschiedlichen IDs wiederverwendet wird, liefert Nuxt beim Navigieren zwischen zwei Detailseiten fälschlicherweise die zwischengespeicherten, alten Daten zurück, statt einen neuen Request auszulösen. Der Key muss deshalb immer die relevanten, variablen Parameter enthalten.
7. Verhalten zwischen Server und Client
Standardmäßig führen beide Composables den Abruf während des Server-Side Renderings aus und übertragen das Ergebnis als Teil des initialen Payloads an den Client. Bei der Hydration im Browser erkennt Nuxt anhand des Keys, dass für diesen Abruf bereits ein Ergebnis vorliegt, und führt den Request nicht erneut aus, sofern server und lazy nicht explizit anders konfiguriert wurden.
Mit der Option server: false lässt sich ein Abruf gezielt auf den Client beschränken, was etwa für Daten sinnvoll ist, die nur im Browser verfügbare Informationen wie localStorage benötigen. Die Option lazy: true wiederum verzögert das Rendering nicht auf das Ergebnis des Abrufs, sondern zeigt die Komponente sofort mit pending: true an und aktualisiert sie, sobald die Daten eintreffen.
8. Die watch-Option: Auf reaktive Parameter reagieren
Hängt ein Abruf von einem reaktiven Wert ab, etwa einer per ref gehaltenen Sortierreihenfolge oder einem Suchbegriff aus einem Eingabefeld, soll der Request idealerweise automatisch neu ausgeführt werden, sobald sich dieser Wert ändert. Beide Composables unterstützen dafür die watch-Option, der eine Liste reaktiver Quellen übergeben wird, deren Änderung einen erneuten Abruf auslöst.
Ohne explizit gesetzte watch-Option reagiert weder useFetch noch useAsyncData automatisch auf Änderungen an Variablen, die innerhalb des Handlers oder der URL verwendet werden, selbst wenn diese Variablen reaktiv sind. Wird dieser Zusammenhang übersehen, bleibt eine Liste nach einer Suchbegriffsänderung fälschlicherweise unverändert stehen, bis die Seite manuell neu geladen wird, was in der Praxis zu einem der verwirrendsten Fehlerbilder bei diesen Composables zählt.
9. Fazit: Zwei Werkzeuge für denselben Zweck
useFetch und useAsyncData lösen dasselbe grundlegende Problem des synchronisierten Datenabrufs zwischen Server und Client, unterscheiden sich aber deutlich im Grad der Kontrolle, den sie bieten. Wer die Wahl bewusst nach der tatsächlichen Komplexität des Anwendungsfalls trifft, statt reflexhaft immer dasselbe Composable zu verwenden, schreibt tendenziell kürzeren und gleichzeitig korrekteren Code.
Besonders wichtig bleibt in beiden Fällen ein durchdachter Caching-Key sowie der bewusste Verzicht auf zusätzliche, manuelle Fetch-Aufrufe außerhalb der Composables. Wer diese beiden Grundregeln beachtet, vermeidet die häufigsten Performance- und Konsistenzprobleme beim Datenabruf in Nuxt-Anwendungen zuverlässig.
| Aspekt | useFetch | useAsyncData |
|---|---|---|
| Caching-Key | Automatisch aus URL/Optionen generiert | Muss explizit angegeben werden |
| Datenquelle | Direkter $fetch-Request | Beliebige asynchrone Handler-Funktion |
| Transform-Option | Vorhanden, über Options-Objekt | Vorhanden, volle Kontrolle im Handler |
| Code-Umfang | Kompakt für Standardfälle | Etwas mehr Code, mehr Flexibilität |
| Typischer Einsatz | Einzelner REST-Endpunkt | Kombinierte Requests, komplexe Logik |
Mironsoft
Vue-Architektur, Composition API und Nuxt-Performance
Vue-Anwendungen, die mit jedem Feature nicht komplizierter werden?
Wir prüfen bestehende Vue- und Nuxt-Projekte auf unstrukturierte Composables, ungenutzte Reaktivität und aufgeblähte Bundles und bauen daraus eine Architektur, die neue Features aufnimmt, ohne die Codebasis unübersichtlicher zu machen.
Architektur-Review
Composables, State-Management und Komponentenstruktur auf Wartbarkeit prüfen.
Performance-Audit
Reaktivitäts-Overhead, Bundle-Größe und Nuxt-Rendering-Strategie systematisch optimieren.
Nuxt-Integration
SSR/SSG-Setup und API-Anbindung robust und typsicher aufbauen.
10. Zusammenfassung
useFetch vs. useAsyncData: Das Wichtigste auf einen Blick
useFetch
Convenience-Wrapper um useAsyncData plus $fetch für den Standardfall
useAsyncData
Volle Kontrolle über Handler, Transform-Funktion und Caching-Key
Häufigster Fehler
Zusätzlicher manueller Fetch in onMounted führt zu doppeltem Abruf
Faustregel
useFetch für einfache Endpunkte, useAsyncData für alles Komplexere