Appearance
Kapitel 35: Pinia (Teil 3) - Datenpersistenz
📙 Lernziel: Pinia State im LocalStorage speichern (Persistenz)!
35.1 Was ist Datenpersistenz?
Datenpersistenz bedeutet, Daten über Browser-Sitzungen hinweg zu speichern.
Anwendungsfälle:
- ✅ Einkaufswagen (Cart)
- ✅ Benutzereinstellungen (Theme, Sprache)
- ✅ Formulardaten (Draft)
- ✅ Login-Status (Token)
Beispiel-Szenario:
Benutzer ändert Theme → Speichern in LocalStorage
Browser neu laden → Theme aus LocalStorage laden35.2 LocalStorage Grundlagen
LocalStorage API:
javascript
// Speichern
localStorage.setItem('key', 'value')
// Laden
const value = localStorage.getItem('key')
// Entfernen
localStorage.removeItem('key')
// Alle entfernen
localStorage.clear()Wichtig:
- ✅ Speichert nur Strings (JSON.stringify() nutzen!)
- ✅ 5MB Speicherplatz (pro Origin)
- ✅ Bleibt auch nach Browser-Neustart erhalten
Beispiel:
javascript
// Objekt speichern
const user = { name: 'Max', age: 25 }
localStorage.setItem('user', JSON.stringify(user))
// Objekt laden
const storedUser = JSON.parse(localStorage.getItem('user'))
console.log(storedUser.name) // 'Max'35.3 Pinia Plugin für Persistenz
Plugin erstellen (plugins/persistedState.js):
javascript
// plugins/persistedState.js
export function persistedState(store) {
// 1. Beim Erstellen: State aus LocalStorage laden
const storedState = localStorage.getItem(store.$id)
if (storedState) {
store.$patch(JSON.parse(storedState))
}
// 2. Bei jeder State-Änderung: In LocalStorage speichern
store.$subscribe((mutation, state) => {
localStorage.setItem(store.$id, JSON.stringify(state))
})
}Verwendung im Store:
javascript
// stores/counter.js
import { defineStore } from 'pinia'
import { persistedState } from '../plugins/persistedState'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const increment = () => {
count.value++
}
return {
count,
increment
}
})
// Plugin registrieren
useCounterStore().$onAction(persistedState)Einfacher (Plugin global registrieren):
javascript
// main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { persistedState } from './plugins/persistedState'
import App from './App.vue'
const pinia = createPinia()
// Plugin für alle Stores registrieren
pinia.use(persistedState)
createApp(App)
.use(pinia)
.mount('#app')35.4 Manuelle Persistenz (Ohne Plugin)
Beispiel: Todo Store mit manueller Persistenz
javascript
// stores/todo.js
import { defineStore } from 'pinia'
import { ref, watch } from 'vue'
export const useTodoStore = defineStore('todo', () => {
// 1. State aus LocalStorage laden (beim Erstellen)
const storedTodos = localStorage.getItem('todos')
const todos = ref(storedTodos ? JSON.parse(storedTodos) : [
{ id: 1, text: 'Lernen', done: false },
{ id: 2, text: 'Sport', done: true }
])
// 2. Watch: Bei jeder Änderung in LocalStorage speichern
watch(todos, (newTodos) => {
localStorage.setItem('todos', JSON.stringify(newTodos))
}, { deep: true })
// Actions
const addTodo = (text) => {
todos.value.push({
id: Date.now(),
text,
done: false
})
}
const removeTodo = (id) => {
todos.value = todos.value.filter(todo => todo.id !== id)
}
const toggleTodo = (id) => {
const todo = todos.value.find(t => t.id === id)
if (todo) {
todo.done = !todo.done
}
}
return {
todos,
addTodo,
removeTodo,
toggleTodo
}
})Verwendung:
vue
<script setup>
import { useTodoStore } from '../stores/todo'
const todoStore = useTodoStore()
const newTodo = ref('')
</script>
<template>
<div>
<h2>Todo Liste</h2>
<input
v-model="newTodo"
@keyup.enter="todoStore.addTodo(newTodo); newTodo = ''"
placeholder="Neue Aufgabe..."
/>
<button @click="todoStore.addTodo(newTodo); newTodo = ''">Hinzufügen</button>
<ul>
<li v-for="todo in todoStore.todos" :key="todo.id">
<input
type="checkbox"
:checked="todo.done"
@change="todoStore.toggleTodo(todo.id)"
/>
<span :style="{ textDecoration: todo.done ? 'line-through' : 'none' }">
{{ todo.text }}
</span>
<button @click="todoStore.removeTodo(todo.id)">Löschen</button>
</li>
</ul>
<button @click="localStorage.removeItem('todos')">Persistenz löschen</button>
</div>
</template>Vorteile manuelle Persistenz:
- ✅ Volle Kontrolle
- ✅ Selektive Persistenz (nur bestimmte Felder)
- ✅ Einfacher zu debuggen
35.5 pinia-plugin-persistedstate (Bibliothek)
Installation:
bash
pnpm add pinia-plugin-persistedstateVerwendung:
javascript
// stores/counter.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
const increment = () => {
count.value++
}
return {
count,
doubleCount,
increment
}
}, {
// Persistenz aktivieren
persist: true
})Konfiguration:
javascript
// stores/counter.js
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const user = ref({ name: '', age: 0 })
return {
count,
user
}
}, {
persist: {
key: 'my-counter', // Custom key
storage: localStorage, // oder sessionStorage
paths: ['count'] // Nur 'count' persistieren (nicht 'user')
}
})Plugin global registrieren:
javascript
// main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { persistedState } from 'pinia-plugin-persistedstate'
import App from './App.vue'
const pinia = createPinia()
// Plugin registrieren
pinia.use(persistedState)
createApp(App)
.use(pinia)
.mount('#app')35.6 Übung: Theme Store mit Persistenz
Aufgabe: Erstelle einen Theme Store, der das Theme im LocalStorage speichert.
Lösung:
1. Store erstellen (stores/theme.js):
javascript
// stores/theme.js
import { defineStore } from 'pinia'
import { ref, watch } from 'vue'
export const useThemeStore = defineStore('theme', () => {
// Aus LocalStorage laden (oder Standard: 'light')
const storedTheme = localStorage.getItem('theme')
const theme = ref(storedTheme || 'light')
// Watch: Bei Änderung in LocalStorage speichern
watch(theme, (newTheme) => {
localStorage.setItem('theme', newTheme)
// Theme auf <html> anwenden
document.documentElement.setAttribute('data-theme', newTheme)
})
// Beim Erstellen: Theme auf <html> anwenden
if (theme.value) {
document.documentElement.setAttribute('data-theme', theme.value)
}
const toggleTheme = () => {
theme.value = theme.value === 'light' ? 'dark' : 'light'
}
return {
theme,
toggleTheme
}
})2. Komponente erstellen (ThemeSwitcher.vue):
vue
<!-- components/ThemeSwitcher.vue -->
<script setup>
import { useThemeStore } from '../stores/theme'
const themeStore = useThemeStore()
</script>
<template>
<div class="theme-switcher">
<p>Aktuelles Theme: {{ themeStore.theme }}</p>
<button @click="themeStore.toggleTheme()">
Wechseln zu {{ themeStore.theme === 'light' ? 'Dark' : 'Light' }} Mode
</button>
</div>
</template>
<style scoped>
.theme-switcher {
position: fixed;
top: 20px;
right: 20px;
padding: 10px 20px;
background: var(--bg-color, white);
color: var(--text-color, black);
border: 1px solid #ddd;
border-radius: 8px;
}
button {
padding: 5px 10px;
cursor: pointer;
}
</style>3. CSS Variablen definieren (style.css):
css
/* style.css */
[data-theme="light"] {
--bg-color: #ffffff;
--text-color: #000000;
}
[data-theme="dark"] {
--bg-color: #1a1a1a;
--text-color: #ffffff;
}
body {
background: var(--bg-color);
color: var(--text-color);
transition: background 0.3s, color 0.3s;
}4. In App.vue verwenden:
vue
<!-- App.vue -->
<script setup>
import ThemeSwitcher from './components/ThemeSwitcher.vue'
</script>
<template>
<div>
<ThemeSwitcher />
<h1>Meine App</h1>
<p>Willkommen!</p>
</div>
</template>Ergebnis:
- ✅ Theme wird im LocalStorage gespeichert
- ✅ Beim Seitenaufruf wird Theme automatisch geladen
- ✅ Sofortige visuelle Änderung
✅ Zusammenfassung
In diesem Kapitel hast du gelernt:
- ✅ Was Datenpersistenz ist
- ✅ LocalStorage Grundlagen
- ✅ Pinia Plugin für Persistenz
- ✅ Manuelle Persistenz (ohne Plugin)
- ✅
pinia-plugin-persistedstateBibliothek - ✅ Praxis: Theme Store mit Persistenz
🎯 Nächster Schritt: In Kapitel 36 lernst du axios (Netzwerkanfragen)!
← Zurück zu Kapitel 34: State, Getters & ActionsWeiter zu Kapitel 36: axios Grundlagen →
