Vue State

Pinia

Pinia ist der offizielle Vue Store: einfach, typsicher und auf der Composition API aufgebaut. State, Getter und Actions an einem Ort, ganz ohne Provider oder Boilerplate.

intermediate12 min readUpdated 15. Sept. 2026
cart.js
js
// stores/cart.js
import { defineStore } from "pinia";
import { ref, computed } from "vue";

export const useCartStore = defineStore("cart", () => {
  const items = ref([]);

  const total = computed(() =>
    items.value.reduce((sum, item) => sum + item.price, 0),
  );

  function add(item) {
    items.value.push(item);
  }

  return { items, total, add };
});
Gewartet von
Dem Vue-Team
Basiert auf
Der Composition API
Stores
defineStore
State
ref / reactive
Abgeleitet
computed
Devtools
Time-travel Support

Warum es wichtig ist

Warum Pinia Vuex ersetzt hat

Einfach durch Design

Ein Store ist eine Funktion, die State und Actions zurückgibt. Es gibt keine Mutations, Module oder Provider, die man lernen muss.

Typsicher

Pinia wurde mit Blick auf TypeScript entwickelt, sodass State und Actions ihre Typen mit fast keinen zusätzlichen Annotationen ableiten.

Composition API native

Setup Stores nutzen ref, computed und Funktionen direkt, sodass der Store-Code wie der Rest deiner Komponenten aussieht.

Das Gesamtbild

Die drei Bestandteile eines Pinia Stores

State, Getter und Actions. In einem Setup Store sind dies einfach refs, computeds und Funktionen.

State

Daten

Reaktive Werte, die mit ref oder reactive deklariert und vom Store zurückgegeben werden.

Getters

Ableitung

Computed-Werte, die aus dem State abgeleitet, gecacht und über Komponenten hinweg geteilt werden.

Actions

Updates

Funktionen, die den State ändern, asynchrone Logik ausführen und andere Stores aufrufen können.

Pinia auf einen Blick

Der Kern von Pinia

defineStore

Erstellt einen Store mit einer eindeutigen ID und entweder einer Setup- oder Options-Funktion.

State

Reaktive Werte, die die in deiner App geteilten Daten halten.

Getters

Aus dem State abgeleitete Computed-Werte, die automatisch gecacht werden.

Actions

Funktionen, die den State aktualisieren und asynchron sein können.

Plugins

Füge Persistenz, Logging oder anderes Verhalten zu jedem Store hinzu.

Stores in Komponenten

Rufe den Store-Hook auf und greife direkt auf seinen State zu, ohne Provider.

Eine kurze Geschichte

Von Vuex zum offiziellen Store

  1. 2019

    Pinia eingeführt

    Ein leichterer, Composition API-freundlicher Store erscheint als Alternative zu Vuex.

    19
  2. 2020

    Vuex 4

    Vuex erscheint für Vue 3, aber das Modulsystem und die Mutations wirken schwerfällig.

    20
  3. 2022

    Offiziell für Vue 3

    Pinia wird zum offiziell empfohlenen Store und wird in neuen Vue-Projekten mitgeliefert.

    22
  4. 2023

    Nuxt-Integration

    Nuxt 3 liefert Pinia-Support aus und importiert Stores automatisch.

    23
  5. Heute

    Der Vue-Standard

    Pinia ist die Standard-State-Lösung für Vue- und Nuxt-Anwendungen.

    Heute

Der vollständige Leitfaden

Pinia: Alles was Sie wissen müssen

Was ist Pinia?

Pinia ist die offizielle State-Management-Library für Vue. Sie hat Vuex als empfohlener Store in Vue 3 abgelöst und ist mittlerweile der Standard in neuen Vue- und Nuxt-Projekten. Pinia ist leichtgewichtig, typsicher und direkt auf der Composition API aufgebaut.

Das Design ist bewusst minimalistisch gehalten. Ein Store ist eine Funktion, die State, abgeleitete Werte und Actions zurückgibt. Es gibt keine Mutations, kein Modul-Namespacing und keinen Provider pro Store. Wenn du ref, computed und Funktionen kennst, beherrschst du bereits den Großteil von Pinia.

Einen Store definieren

Ein Store wird mit defineStore erstellt, wobei eine eindeutige ID und eine Setup-Funktion übergeben werden.

// stores/cart.js
import { defineStore } from "pinia";
import { ref, computed } from "vue";

export const useCartStore = defineStore("cart", () => {
  const items = ref([]);

  const total = computed(() =>
    items.value.reduce((sum, item) => sum + item.price, 0),
  );

  const count = computed(() => items.value.length);

  function add(item) {
    items.value.push(item);
  }

  function remove(id) {
    items.value = items.value.filter((item) => item.id !== id);
  }

  return { items, total, count, add, remove };
});

Dies ist ein Setup Store: ref wird zum State, computed wird zu einem Getter und Funktionen werden zu Actions. Die eindeutige ID benennt den Store in den Devtools und ist erforderlich.

Pinia unterstützt auch einen Options Store, falls Sie die Vuex-ähnliche Struktur bevorzugen:

// stores/counter.js
export const useCounter = defineStore("counter", {
  state: () => ({ count: 0 }),
  getters: {
    double: (state) => state.count * 2,
  },
  actions: {
    increment() {
      this.count += 1;
    },
  },
});

Beide Formen werden vollständig unterstützt. Setup Stores passen tendenziell besser zu TypeScript und der Composition API, während sich Options Stores für Vuex-Nutzer vertrauter anfühlen können.

Verwendung eines Stores in Komponenten

Rufen Sie die Store-Funktion auf, um die Store-Instanz zu erhalten. Es gibt keinen Provider, mit dem Sie Ihre Komponenten umschließen müssen.

<!-- Cart.vue -->
<script setup>
import { storeToRefs } from "pinia";
import { useCartStore } from "@/stores/cart";

const cart = useCartStore();
const { items, total } = storeToRefs(cart);
</script>

<template>
  <ul>
    <li v-for="item in items" :key="item.id">
      {{ item.name }} — {{ item.price }}
      <button @click="cart.remove(item.id)">Remove</button>
    </li>
  </ul>
  <p>Total: {{ total }}</p>
</template>

Das direkte Lesen von cart.total in einem Template bleibt reaktiv. Wenn Sie State oder Getter destrukturieren möchten und dabei die Reaktivität beibehalten wollen, verwenden Sie storeToRefs, welches diese in refs konvertiert. Actions können direkt destrukturiert werden, da sie keine Reaktivität benötigen.

Getter

Getter sind berechnete Werte, die aus dem State abgeleitet werden. Sie werden gecacht und gemeinsam genutzt, sodass jede Komponente, die denselben Getter liest, dieselbe Berechnung verwendet.

// stores/products.js
export const useProductsStore = defineStore("products", () => {
  const products = ref([]);
  const filter = ref("");

  const visible = computed(() =>
    products.value.filter((p) =>
      p.name.toLowerCase().includes(filter.value.toLowerCase()),
    ),
  );

  const inStock = computed(() =>
    visible.value.filter((p) => p.stock > 0),
  );

  return { products, filter, visible, inStock };
});

Es ist ein häufiger Fehler, auf watch zurückzugreifen, um einen Wert zu berechnen. Wenn dieser aus dem bestehenden State abgeleitet werden kann, sollte er ein Getter sein – genau so, wie Sie computed in einer Komponente verwenden würden.

Actions

Actions sind Funktionen, die den State ändern. Sie können synchron oder asynchron sein und können andere Actions oder sogar andere Stores aufrufen.

// stores/users.js
export const useUsersStore = defineStore("users", () => {
  const users = ref([]);
  const loading = ref(false);
  const error = ref(null);

  async function fetchUsers() {
    loading.value = true;
    error.value = null;
    try {
      const res = await fetch("/api/users");
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      users.value = await res.json();
    } catch (err) {
      error.value = err.message;
    } finally {
      loading.value = false;
    }
  }

  return { users, loading, error, fetchUsers };
});

Für Server-Daten empfiehlt es sich jedoch, eine Data-Fetching-Layer wie TanStack Query zu verwenden, welche das Caching und die Invalidierung übernimmt. Pinia eignet sich am besten für den Client-State: Warenkörbe, Filter, UI-Einstellungen, Auth-State und Entwürfe.

Plugins und Persistenz

Pinia-Plugins werden für jeden Store ausgeführt und können Funktionen wie Persistenz, Logging oder das Zurücksetzen von Zuständen hinzufügen.

// persist.js
export function persistPlugin({ store }) {
  const saved = localStorage.getItem(store.$id);
  if (saved) store.$patch(JSON.parse(saved));

  store.$subscribe((_mutation, state) => {
    localStorage.setItem(store.$id, JSON.stringify(state));
  });
}

// main.js
const pinia = createPinia();
pinia.use(persistPlugin);
app.use(pinia);

Das beliebte pinia-plugin-persistedstate bietet dieselbe Funktionalität mit Optionen dazu, welche Keys gespeichert werden sollen. Persistenz ist einer der Hauptgründe, warum man einen Store anstelle eines lokalen Component-States verwendet.

Pinia mit Nuxt

Installiere in Nuxt das offizielle Modul; Stores in stores/ werden dann automatisch importiert.

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ["@pinia/nuxt"],
});
<!-- pages/cart.vue -->
<script setup>
const cart = useCartStore(); // auto-imported
</script>

Da Nuxt die Installation und das Server-Rendering übernimmt, funktioniert derselbe Store sowohl auf dem Server als auch auf dem Client. Siehe den Nuxt-Guide dazu, wie dies mit useFetch und Server-Routes zusammenpasst.

Best Practices

  • Bevorzugen Sie Setup Stores für neuen Code, insbesondere in Verbindung mit TypeScript.
  • Halten Sie abgeleitete Daten in Gettern, anstatt den State zu beobachten.
  • Verwenden Sie storeToRefs beim Destrukturieren von State oder Gettern.
  • Behalten Sie Server-Daten in einem Data-Fetching-Layer und nicht in Pinia.
  • Nutzen Sie Actions für alles, was den State ändert, einschließlich asynchroner Operationen.
  • Persistieren Sie nur den State, der einen Reload überstehen soll.
  • Benennen Sie Stores klar nach der Domäne, die sie verwalten.

Häufige Fehler

  • Destructuring des State ohne storeToRefs, wodurch die Reaktivität verloren geht.
  • Verwendung von watch zur Berechnung eines Wertes, den eigentlich ein Getter ableiten sollte.
  • Speichern von abgerufenen Server-Daten im Store und manuelles Verwalten von Loading-Flags.
  • Vergessen, Pinia zu installieren, sodass Store-Aufrufe zur Laufzeit fehlschlagen.
  • Erstellung eines einzigen riesigen Stores anstelle von fokussierten Domain-Stores.
  • Mutation des State außerhalb einer Action, was die Nachverfolgung erschwert.

Wie geht es weiter?

Pinia ist das Vue-Äquivalent zu einem kleinen, modernen Store. Vertiefe dein Vue-Wissen, ergänze Nuxt für Server Rendering und Auto-Imports und vergleiche diesen Ansatz mit der Context API, Zustand und Redux Toolkit von React. Erstelle anschließend einen kleinen Store mit einem Getter, einer async Action und Persistence, um zu sehen, wie wenig Code dafür nötig ist.

Einen Store definieren

Ein Setup Store verwendet dieselbe ref, computed und Funktions-Syntax wie deine Komponenten, sodass es weniger zu lernen gibt.

Setup Store
export const useCounter = defineStore(
  "counter",
  () => {
    const count = ref(0);
    const double = computed(
      () => count.value * 2,
    );
    const inc = () => count.value++;
    return { count, double, inc };
  },
);
Options Store
export const useCounter = defineStore(
  "counter",
  {
    state: () => ({ count: 0 }),
    getters: {
      double: (s) => s.count * 2,
    },
    actions: {
      inc() { this.count++; },
    },
  },
);

Abgeleitete Werte

Nutze Getter (computed) für Werte, die aus dem State abgeleitet werden. Beobachte den State nicht nur, um einen berechneten Wert zu speichern.

Bevorzugt
const total = computed(() =>
  items.value.reduce(
    (sum, i) => sum + i.price, 0,
  ),
);
Vermeiden
const total = ref(0);
watch(items, () => {
  total.value = items.value.reduce(
    (sum, i) => sum + i.price, 0,
  );
});

Häufig gestellte Fragen

Häufig gestellte Fragen

Keep learning

Related topics from the roadmap.

$ Lernen Sie jetzt

Bereit, Pinia zu lernen?

Unser interaktives Tutorial führt Sie Schritt für Schritt durch Pinia — mit Quizzen und echtem Code, den Sie im Browser ausführen können.