Appearance
Kapitel 6: Datenabfrage & Zustandsverwaltung
In diesem Kapitel lernen Sie, wie Sie Daten von APIs abrufen und den Anwendungszustand in Nuxt 3 verwalten.
6.1 Drei Möglichkeiten der Datenabfrage
Nuxt 3 bietet drei Hauptmethoden zum Abrufen von Daten:
Methode 1: useAsyncData() (Empfohlen für komplexe Logik)
useAsyncData() ist die mächtigste Methode zur Datenabfrage.
Einfaches Beispiel:
vue
<script setup>
// Asynchrone Datenabfrage
const { data: posts, pending, error, refresh } = await useAsyncData(
'posts', // Eindeutiger Schlüssel (für Caching)
() => $fetch('/api/posts') // Asynchrone Funktion
)
</script>
<template>
<div>
<!-- Ladezustand -->
<div v-if="pending">Lädt...</div>
<!-- Fehlerbehandlung -->
<div v-else-if="error">Fehler: {{ error.message }}</div>
<!-- Daten anzeigen -->
<div v-else>
<h1>Blogbeiträge</h1>
<ul>
<li v-for="post in posts" :key="post.id">
{{ post.title }}
</li>
</ul>
</div>
<!-- Manuelles Aktualisieren -->
<button @click="refresh()">Aktualisieren</button>
</div>
</template>Parameter von useAsyncData():
| Parameter | Typ | Beschreibung |
|---|---|---|
key | string | Eindeutiger Schlüssel für Caching |
handler | Function | Asynchrone Funktion zum Abrufen der Daten |
options | Object | Zusätzliche Optionen (siehe unten) |
Optionen:
javascript
const { data } = await useAsyncData('posts', () => $fetch('/api/posts'), {
lazy: false, // Warten auf Daten vor dem Rendern (Standard)
server: true, // Auf dem Server ausführen (SSR)
client: true, // Im Client ausführen
immediate: true, // Sofort ausführen
watch: [] // Überwachen von reaktiven Werten
})Methode 2: useFetch() (Vereinfachte Version)
useFetch() ist eine Abkürzung für useAsyncData() + $fetch().
Einfaches Beispiel:
vue
<script setup>
// Einfacher Datenabruf
const { data: posts, pending, error } = await useFetch('/api/posts')
</script>
<template>
<div>
<div v-if="pending">Lädt...</div>
<div v-else-if="error">Fehler!</div>
<div v-else>
<pre>{{ posts }}</pre>
</div>
</div>
</template>Mit Optionen:
vue
<script setup>
const { data } = await useFetch('/api/posts', {
method: 'GET',
params: {
page: 1,
limit: 10
},
headers: {
'Authorization': 'Bearer token'
}
})
</script>Methode 3: useLazyFetch() / useLazyAsyncData()
Diese Methoden führen die Datenabfrage nicht blockierend aus. Die Seite wird sofort gerendert, und die Daten werden später geladen.
Beispiel:
vue
<script setup>
// Lazy Loading (nicht blockierend)
const { data: posts, pending } = await useLazyFetch('/api/posts')
</script>
<template>
<div>
<!-- Sofort sichtbar -->
<h1>Blog</h1>
<!-- Ladeanimation -->
<div v-if="pending">Lädt Posts...</div>
<!-- Daten -->
<div v-else>
<div v-for="post in posts" :key="post.id">
{{ post.title }}
</div>
</div>
</div>
</template>6.2 Datenanfrage kapseln (Request Wrapper)
Für produktionsreife Anwendungen sollten Sie API-Aufrufe zentral verwalten.
Zentraler API-Service erstellen:
utils/api.ts (im utils/-Verzeichnis):
typescript
// Zentraler API-Wrapper
export const useAPI = () => {
const config = useRuntimeConfig()
// Basis-URL
const baseURL = config.public.apiBase || '/api'
// GET-Anfrage
const get = async (endpoint: string, options = {}) => {
return await $fetch(`${baseURL}/${endpoint}`, {
method: 'GET',
...options
})
}
// POST-Anfrage
const post = async (endpoint: string, body: any, options = {}) => {
return await $fetch(`${baseURL}/${endpoint}`, {
method: 'POST',
body,
...options
})
}
// PUT-Anfrage
const put = async (endpoint: string, body: any, options = {}) => {
return await $fetch(`${baseURL}/${endpoint}`, {
method: 'PUT',
body,
...options
})
}
// DELETE-Anfrage
const del = async (endpoint: string, options = {}) => {
return await $fetch(`${baseURL}/${endpoint}`, {
method: 'DELETE',
...options
})
}
return {
get,
post,
put,
delete: del
}
}Verwendung im Composable:
composables/usePosts.ts:
typescript
export const usePosts = () => {
const api = useAPI()
// Alle Posts abrufen
const fetchPosts = async () => {
return await api.get('posts')
}
// Einzelnen Post abrufen
const fetchPost = async (id: string) => {
return await api.get(`posts/${id}`)
}
// Post erstellen
const createPost = async (postData: any) => {
return await api.post('posts', postData)
}
return {
fetchPosts,
fetchPost,
createPost
}
}Verwendung in einer Komponente:
vue
<script setup>
const { fetchPosts } = usePosts()
// Daten abrufen
const { data: posts, pending } = await useAsyncData(
'posts',
() => fetchPosts()
)
</script>
<template>
<div>
<h1>Blogbeiträge</h1>
<div v-if="pending">Lädt...</div>
<ul v-else>
<li v-for="post in posts" :key="post.id">
{{ post.title }}
</li>
</ul>
</div>
</template>6.3 Zustandsverwaltung (State Management)
Nuxt 3 bietet zwei Hauptmethoden zur Zustandsverwaltung:
Methode 1: useState() (Einfach, integriert)
useState() ist eine integrierte Methode zur Zustandsverwaltung. Sie ist SSR-kompatibel und teilt den Zustand zwischen Server und Client.
Globaler Zustand (Composable):
composables/useCounter.ts:
typescript
export const useCounter = () => {
// Globaler Zustand (überall verfügbar)
const count = useState('counter', () => 0)
// Zähler erhöhen
const increment = () => {
count.value++
}
// Zähler zurücksetzen
const reset = () => {
count.value = 0
}
return {
count: readonly(count), // Schreibgeschützt exportieren
increment,
reset
}
}Verwendung in Komponenten:
vue
<script setup>
const { count, increment, reset } = useCounter()
</script>
<template>
<div>
<p>Zähler: {{ count }}</p>
<button @click="increment">+1</button>
<button @click="reset">Zurücksetzen</button>
</div>
</template>Methode 2: Pinia (Für komplexe Anwendungen)
Für komplexe Zustandslogik empfiehlt sich Pinia (offizielle Vue 3 State-Management-Bibliothek).
Pinia installieren:
bash
pnpm add @pinia/nuxtIn nuxt.config.ts konfigurieren:
typescript
export default defineNuxtConfig({
modules: [
'@pinia/nuxt'
]
})Pinia Store erstellen:
stores/counter.ts:
typescript
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
todos: []
}),
getters: {
doubled: (state) => state.count * 2
},
actions: {
increment() {
this.count++
},
addTodo(todo: string) {
this.todos.push(todo)
}
}
})Verwendung in Komponenten:
vue
<script setup>
import { useCounterStore } from '~/stores/counter'
const store = useCounterStore()
</script>
<template>
<div>
<p>Zähler: {{ store.count }}</p>
<p>Verdoppelt: {{ store.doubled }}</p>
<button @click="store.increment()">+1</button>
</div>
</template>6.4 Lokaler Speicher (Local Storage & Cookies)
useCookie() (Cookies verwalten)
Nuxt 3 bietet das Composable useCookie() zur Cookie-Verwaltung.
Beispiel:
vue
<script setup>
// Cookie setzen (läuft auf Server & Client)
const userToken = useCookie('token', {
maxAge: 60 * 60 * 24, // 1 Tag
secure: true,
sameSite: 'strict'
})
// Cookie setzen
const setToken = () => {
userToken.value = 'abc123'
}
// Cookie lesen
console.log(userToken.value)
// Cookie löschen
const removeToken = () => {
userToken.value = null
}
</script>
<template>
<div>
<button @click="setToken">Token setzen</button>
<button @click="removeToken">Token löschen</button>
</div>
</template>LocalStorage (Nur Client-Seite!)
Achtung: localStorage ist nicht auf dem Server verfügbar (SSR).
Sichere Verwendung:
vue
<script setup>
const userPreferences = ref(null)
// Nur im Client ausführen!
onMounted(() => {
const stored = localStorage.getItem('preferences')
if (stored) {
userPreferences.value = JSON.parse(stored)
}
})
// Speichern
const savePreferences = () => {
localStorage.setItem('preferences', JSON.stringify(userPreferences.value))
}
</script>6.5 Zusammenfassung
In diesem Kapitel haben Sie gelernt:
- ✅ Drei Methoden zur Datenabfrage (
useAsyncData,useFetch,useLazyFetch) - ✅ API-Anfragen zentral zu kapseln (Request Wrapper)
- ✅ Zustandsverwaltung mit
useState()(einfach) und Pinia (komplex) - ✅ Cookies mit
useCookie()zu verwalten - ✅ LocalStorage sicher zu verwenden (nur Client-Seite)
Nächste Schritte: Im nächsten Kapitel lernen wir Layouts & Komponentenentwicklung – wie man wiederverwendbare Komponenten erstellt und Layouts verwendet.
📚 Weiterführende Ressourcen
- useAsyncData() Dokumentation
- useFetch() Dokumentation
- useState() Dokumentation
- Pinia Dokumentation
- useCookie() Dokumentation
Nächstes Kapitel: Kapitel 7: Layouts & Komponentenentwicklung →
