Pinia
Pinia ist die offizielle State-Management-Lösung für Vue 3. Es ersetzt Vuex und bietet eine einfachere, typsichere API.
Kernkonzepte:
- State - Zentrale Datenhaltung
- Getters - Berechnete Werte
- Actions - Methoden & Async
Installation
Abschnitt betitelt „Installation“npm install piniaimport { createApp } from 'vue'import { createPinia } from 'pinia'import App from './App.vue'
const app = createApp(App)const pinia = createPinia()
app.use(pinia)app.mount('#app')Store definieren: Setup Syntax
Abschnitt betitelt „Store definieren: Setup Syntax“import { ref, computed } from 'vue'import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => { // State const count = ref(0)
// Getters const doubleCount = computed(() => count.value * 2)
// Actions function increment() { count.value++ }
async function incrementAsync() { await new Promise(r => setTimeout(r, 1000)) count.value++ }
return { count, doubleCount, increment, incrementAsync }})Store definieren: Options Syntax
Abschnitt betitelt „Store definieren: Options Syntax“import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', { state: () => ({ count: 0, name: 'Counter' }),
getters: { doubleCount: (state) => state.count * 2, // Getter mit Parametern countPlusN: (state) => (n) => state.count + n },
actions: { increment() { this.count++ }, async fetchAndSet() { const data = await api.fetchCount() this.count = data.count } }})Setup vs Options Syntax
Abschnitt betitelt „Setup vs Options Syntax“| Aspekt | Setup Syntax | Options Syntax |
|---|---|---|
| State | ref(), reactive() | state: () => ({}) |
| Getters | computed() | getters: {} |
| Actions | Funktionen | actions: {} |
| TypeScript | Automatisch | Mehr Boilerplate |
| Composables | Direkt nutzbar | In Actions |
| Stil | Composition API | Options API |
Store verwenden
Abschnitt betitelt „Store verwenden“<script setup>import { useCounterStore } from '@/stores/counter'
// Store-Instanzconst counterStore = useCounterStore()
// Direkter Zugriffconsole.log(counterStore.count)console.log(counterStore.doubleCount)
// Actions aufrufenfunction handleClick() { counterStore.increment()}</script>
<template> <p>Count: {{ counterStore.count }}</p> <p>Double: {{ counterStore.doubleCount }}</p> <button @click="counterStore.increment()">+1</button></template>Destrukturierung mit storeToRefs
Abschnitt betitelt „Destrukturierung mit storeToRefs“<script setup>import { storeToRefs } from 'pinia'import { useCounterStore } from '@/stores/counter'
const counterStore = useCounterStore()
// FALSCH: Reaktivität geht verloren!const { count, doubleCount } = counterStore
// RICHTIG: storeToRefs für State & Gettersconst { count, doubleCount } = storeToRefs(counterStore)
// Actions können direkt destrukturiert werdenconst { increment, incrementAsync } = counterStore</script>
<template> <p>{{ count }}</p> <button @click="increment">+1</button></template>State modifizieren
Abschnitt betitelt „State modifizieren“<script setup>import { useCounterStore } from '@/stores/counter'
const store = useCounterStore()
// Direkte Mutationstore.count++
// Mit $patch (Objekt) - effizienter bei mehreren Änderungenstore.$patch({ count: store.count + 1, name: 'New Name'})
// Mit $patch (Funktion) - für komplexe Mutationenstore.$patch((state) => { state.items.push({ id: 1, name: 'Item' }) state.count++})
// State komplett ersetzenstore.$reset() // Zurück zum Initialzustand</script>Praktisches Beispiel: Auth Store
Abschnitt betitelt „Praktisches Beispiel: Auth Store“import { ref, computed } from 'vue'import { defineStore } from 'pinia'import { authApi } from '@/api/auth'
export const useAuthStore = defineStore('auth', () => { const user = ref(null) const token = ref(localStorage.getItem('token'))
const isLoggedIn = computed(() => !!token.value) const userName = computed(() => user.value?.name ?? 'Gast')
async function login(credentials) { const response = await authApi.login(credentials) token.value = response.token user.value = response.user localStorage.setItem('token', response.token) }
function logout() { token.value = null user.value = null localStorage.removeItem('token') }
return { user, token, isLoggedIn, userName, login, logout }})<script setup>import { useAuthStore } from '@/stores/auth'import { useRouter } from 'vue-router'
const auth = useAuthStore()const router = useRouter()
async function handleLogin(credentials) { try { await auth.login(credentials) router.push({ name: 'dashboard' }) } catch (error) { console.error('Login fehlgeschlagen:', error) }}
function handleLogout() { auth.logout() router.push({ name: 'home' })}</script>
<template> <div v-if="auth.isLoggedIn"> <p>Willkommen, {{ auth.userName }}!</p> <button @click="handleLogout">Logout</button> </div></template>Stores in anderen Stores
Abschnitt betitelt „Stores in anderen Stores“import { ref, computed } from 'vue'import { defineStore } from 'pinia'import { useAuthStore } from './auth'
export const useCartStore = defineStore('cart', () => { const items = ref([])
const total = computed(() => items.value.reduce((sum, item) => sum + item.price * item.quantity, 0) )
async function checkout() { const auth = useAuthStore() // Anderen Store verwenden
if (!auth.isLoggedIn) { throw new Error('Bitte einloggen') }
await api.checkout({ userId: auth.user.id, items: items.value })
items.value = [] }
return { items, total, checkout }})State subscriben
Abschnitt betitelt „State subscriben“<script setup>import { useCounterStore } from '@/stores/counter'import { watch } from 'vue'
const store = useCounterStore()
// Mit $subscribe - alle Mutationenstore.$subscribe((mutation, state) => { console.log('Mutation:', mutation.type) console.log('Neuer State:', state)
// z.B. in localStorage speichern localStorage.setItem('counter', JSON.stringify(state))})
// Oder mit watch auf spezifische Wertewatch( () => store.count, (newCount) => { console.log('Count geändert:', newCount) })</script>Pinia vs Vuex
Abschnitt betitelt „Pinia vs Vuex“- Mutations + Actions
- Komplexer Boilerplate
- Schwache TypeScript-Unterstützung
- Globale Module
commit(),dispatch()
mutations: { SET_COUNT(state, value) { state.count = value }},actions: { increment({ commit }) { commit('SET_COUNT', ...) }}- Nur Actions (Mutations entfallen)
- Minimaler Boilerplate
- Volle TypeScript-Unterstützung
- Modulare Stores
- Direkte Aufrufe
// Einfach und direktfunction increment() { count.value++}DevTools Integration
Abschnitt betitelt „DevTools Integration“Pinia integriert sich nahtlos in die Vue DevTools:
Features:
- Store-State inspizieren
- Getters anzeigen
- Actions verfolgen
- Time-Travel Debugging
- State bearbeiten
// Stores benennen für bessere DevTools-AnzeigedefineStore('auth', () => { ... })defineStore('cart', () => { ... })defineStore('products', () => { ... })Best Practices
Abschnitt betitelt „Best Practices“- Ein Store pro Domain/Feature
- Setup Syntax für Konsistenz
storeToRefs()für Destrukturierung- Stores in
stores/Ordner - Actions für async Logik
- Computed für abgeleitete Werte
- Alles in einen Store
- State direkt in Templates mutieren
- Stores in Composables cachen
- Zirkuläre Store-Abhängigkeiten
- State ohne Actions ändern (außer einfache Fälle)
Dokumentation
Abschnitt betitelt „Dokumentation“Offizielle Dokumentation:
- Getting Started - Einführung
- Defining a Store - Store-Definition
- State - State Management
- Plugins - Erweiterungen
Ressourcen:
- pinia.vuejs.org - Offizielle Docs
- pinia-plugin-persistedstate - Persistenz Plugin