Pinia — Globale Zustandsverwaltung

In diesem Kapitel lernst du, Pinia zu verwenden, um gemeinsame Daten über Komponenten und Seiten hinweg zu verwalten, und die Funktion "Lieblingsartikel" zu implementieren.


Warum brauchen wir eine globale Zustandsverwaltung?

Bisher wurden Daten zwischen Komponenten über props und emit weitergegeben.

Das reicht für einfache Eltern-Kind-Szenarien aus, wird aber in den folgenden Situationen umständlich:

  • Der Favoritenstatus muss zwischen der Startseite und der Detailseite synchronisiert werden.
  • Der Benutzer-Anmeldestatus muss über alle Seiten hinweg geteilt werden.
  • Warenkorbdaten müssen zwischen der Navigationsleiste, der Produktseite und der Checkout-Seite übergeben werden.

Weitergabe von Props Ebene für EbeneDer Schmerzpunkt: Wenn Daten in Komponente A an Komponente D übergeben werden müssen, müssen sie B und C als Relais passieren, obwohl diese Zwischenkomponenten die Daten selbst nicht benötigen.

PiniaPinia ist die von Vue 3 offiziell empfohlene State-Management-Bibliothek. Sie bietet ein globales "Datenzentrum", in dem jede Komponente Daten direkt lesen und ändern kann, ohne schichtweise Weitergabe.


Pinia installieren und registrieren

Wenn du beim Erstellen des Projekts bereits Pinia ausgewählt hast, überspringe die Installationsschritte.

Andernfalls:

$ npm install pinia

Registriere es in main.js:

Beispiel

// Dateipfad: src/main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'

const app = createApp(App)

// Eine Pinia-Instanz erstellen und registrieren
const pinia = createPinia()
app.use(pinia)
app.use(router)
app.mount('#app')

defineStore — Definieren eines Stores

Der Kern von Pinia ist derStoreJeder Store verwaltet eine unabhängige Zustandseinheit.

Es besteht aus drei Teilen:

TeilZweckAnalogie
stateStore-DatenDaten / ref der Komponente
GetterAbgeleitete berechnete DatenComputed der Komponente
actionsMethoden zum Ändern von DatenMethoden / Funktionen der Komponente

Beispiel

// Dateipfad: src/stores/useFavoriteStore.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

// defineStore('storeName', () => { ... })
// Der zweite Parameter verwendet eine Funktionssyntax, konsistent mit dem Composition-API-Stil.
export const useFavoriteStore = defineStore('favorite', () => {
  // ====== State: State mit ref definieren ======
  // Verwende ein Array, um die IDs der favorisierten Artikel zu speichern
  const favoriteIds = ref([])

  // Die Favoritenliste aus localStorage wiederherstellen
  const saved = localStorage.getItem('blog-favorites')
  if (saved) {
    favoriteIds.value = JSON.parse(saved)
  }

  // ====== Getter: berechnete Eigenschaften mit computed definieren ======
  const favoriteCount = computed(() => favoriteIds.value.length)

  // Ermitteln, ob ein Artikel als Favorit markiert wurde
  function isFavorite(articleId) {
    return favoriteIds.value.includes(articleId)
  }

  // ====== Aktionen: Änderungsmethoden mit regulären Funktionen definieren ======
  // Favoritenstatus umschalten (favorisiert → nicht favorisiert, nicht favorisiert → favorisiert)
  function toggleFavorite(articleId) {
    const index = favoriteIds.value.indexOf(articleId)
    if (index === -1) {
      favoriteIds.value.push(articleId)     // Favorit hinzufügen
    } else {
      favoriteIds.value.splice(index, 1)    // Favorit entfernen
    }
    // Mit localStorage synchronisieren (persistente Speicherung)
    localStorage.setItem('blog-favorites', JSON.stringify(favoriteIds.value))
  }

  return { favoriteIds, favoriteCount, isFavorite, toggleFavorite }
})

Der zweite Parameter von defineStore kann auf zwei Arten geschrieben werden:Options Store(ähnlich der data/methods/computed-Objektsyntax von Vue 2) undSetup Store(eine Funktionssyntax unter Verwendung der Composition API). Es wird empfohlen, den Setup Store zu verwenden, der mit der<script setup>Syntax übereinstimmt, die in Komponenten verwendet wird, und den geringsten Lernaufwand hat.


Verwendung des Stores in Komponenten

Jede Komponente kannuseFavoriteStore()eine Store-Instanz abrufen und dann direkt den State lesen/schreiben und Actions aufrufen.

Hinzufügen eines Favoriten-Buttons in BlogCard

Beispiel

<!-- Dateipfad: src/components/BlogCard.vue -->
<script setup>
import { useFavoriteStore } from '../stores/useFavoriteStore.js'

const props = defineProps({
  id: Number,
  title: String,
  summary: String,
  date: String,
  category: String
})

const favoriteStore = useFavoriteStore()

// Wird aufgerufen, wenn auf die Favoriten-Schaltfläche geklickt wird
function handleFavorite(e) {
e.preventDefault() // Verhindert, dass RouterLink navigiert
  favoriteStore.toggleFavorite(props.id)
}
</script>

<template>
  <RouterLink :to="{ name: 'post', params: { id } }" class="card-link">
    <div class="card">
      <span class="tag">{{ category }}</span>
      <h3>{{ title }}</h3>
      <p>{{ summary }}</p>
      <div class="card-footer">
        <span class="date">{{ date }}</span>
        <!-- Favoriten-Schaltfläche: ausgefüllt, wenn als Favorit markiert, umrandet, wenn nicht als Favorit markiert -->
        <button class="fav-btn" @click="handleFavorite">
          {{ favoriteStore.isFavorite(id) ? '&#x2665;' : '♡' }}
        </button>
      </div>
    </div>
  </RouterLink>
</template>

<style scoped>
.card-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 10px;
}
.fav-btn {
  background: none;
  border: none;
  font-size: 20px;
  cursor: pointer;
  color: #e74c3c;
  padding: 4px 8px;
}
.fav-btn:hover {
  transform: scale(1.2);
}
</style>

Füge außerdem einen Favoriten-Button auf der Detailseite hinzu

Beispiel

<!-- Dateipfad: src/views/PostView.vue (Teilcode) -->
<script setup>
import { useFavoriteStore } from '../stores/useFavoriteStore.js'

const favoriteStore = useFavoriteStore()
// ... sonstiger Code
</script>

<template>
  <article v-if="article">
    <div class="post-header">
      <h1>{{ article.title }}</h1>
      <!-- Favoriten-Button auf der Detailseite: Zustand vollständig mit der Startseite synchronisiert -->
      <button class="fav-btn" @click="favoriteStore.toggleFavorite(article.id)">
        {{ favoriteStore.isFavorite(article.id) ? '&#x2665;Favorisiert' : '♡ Favorisieren' }}
      </button>
    </div>
    <!-- ... -->
  </article>
</template>

Der Favoritenstatus auf der Startseite und der Detailseite ist vollständig synchronisiert: Nachdem du einen Artikel auf der Startseite favorisiert hast, wird beim Aufrufen der Detailseite automatisch "Favorisiert" angezeigt.

Das ist der Wert der globalen Zustandsverwaltung: Daten werden an einem einzigen Ort gespeichert, und alle Komponenten lesen und schreiben dieselbe Quelle. Es ist keine emit-Weitergabe oder schichtweise Props-Weiterleitung nötig.


Anzeigen der Favoritenanzahl in NavBar

Zeige die Anzahl der Favoriten auf der rechten Seite der Navigationsleiste an; klicke darauf, um die Liste der favorisierten Artikel zu sehen.

Beispiel

<!-- Dateipfad: src/components/NavBar.vue -->
<script setup>
import { useFavoriteStore } from '../stores/useFavoriteStore.js'

const favoriteStore = useFavoriteStore()
</script>

<template>
  <header class="navbar">
    <a href="/" class="logo">BEISPIEL Blog</a>
    <nav>
      <!-- Badge für Favoritenanzahl -->
      <span class="fav-badge" v-if="favoriteStore.favoriteCount > 0">
Favoriten {{ favoriteStore.favoriteCount }}
      </span>
    </nav>
  </header>
</template>

<style scoped>
.fav-badge {
  background: #e74c3c;
  color: #fff;
  padding: 4px 10px;
  border-radius: 12px;
  font-size: 13px;
}
</style>

Die Anzahl in NavBar bleibt automatisch mit der neuesten Favoritenliste synchronisiert – das Favorisieren oder Entfernen eines Artikels aus den Favoriten ändert sofort die Zahl in der Navigationsleiste.


Zusammenfassung der drei Kernfähigkeiten von Pinia

FähigkeitEntsprechender CodeAnwendung im Favoriten-Feature
Globales TeilenJede Komponente kann useXxxStore() verwendenNavBar, BlogCard und PostView teilen dieselbe Favoritenliste.
ReaktivitätWenn sich der Zustand ändert, werden alle Konsumenten automatisch aktualisiert.Nach dem Favorisieren wird die Schaltfläche auf der Startseite rot, der Zähler in der Navigationsleiste erhöht sich um 1, und die Detailseite wird synchron aktualisiert.
PersistenzIn actions in localStorage schreibenDie Favoritenliste existiert nach dem Aktualisieren der Seite weiterhin.

Kapitelzusammenfassung

In diesem Kapitel haben Sie die vollständige Verwendung von Pinia für die globale Zustandsverwaltung gemeistert: Definieren eines Stores mit defineStore (state/getters/actions), Abrufen einer Instanz mit useXxxStore in Komponenten und Implementieren der Datenpersistenz durch localStorage.

Das Feature "Lieblingsartikel" macht die Datensynchronisierung zwischen Startseite, Detailseite und Navigationsleiste sehr natürlich, und alle Komponenten bleiben automatisch konsistent.

Andere Erweiterungen