vue pinia is de officiële en tegenwoordig aanbevolen manier om gedeelde state (toestand) te beheren in Vue 3-applicaties. Wanneer meerdere componenten dezelfde data nodig hebben — de ingelogde gebruiker, de winkelwagen, de themavoorkeur — wil je die op één centrale plek bewaren. De oude standaard hiervoor was Vuex, maar het Vue-team raadt nu Pinia aan voor nieuwe projecten. In dit artikel zien we stap voor stap hoe je vanaf nul een store bouwt met Pinia in plaats van Vuex.
Waarom Pinia in plaats van Vuex?
Pinia is de officiële state-managementbibliotheek die Vuex vervangt, en de voorkeur ervoor komt omdat het een paar concrete problemen oplost:
- Minder ceremonie: in Vuex moest je
mutationsschrijven om iets te wijzigen. Pinia laat het mutation-concept volledig vallen; je wijzigt state direct of vanuitactions. - Volledige TypeScript-ondersteuning: types worden automatisch afgeleid;
thisen retourwaarden zijn getypeerd zonder extra boilerplate. - Geen modules, gewoon platte stores: in plaats van de geneste modulestructuur van Vuex is elke store een onafhankelijke eenheid. Je roept elke store overal aan.
- Gemaakt voor de Composition API: Pinia werkt natuurlijk samen met de
setup-logica van Vue 3 en heeft devtools-ondersteuning.
Kortom, Pinia doet alles wat Vuex deed met minder code en betere typeveiligheid. Heb je al een Vuex-project, dan hoef je niet geforceerd te migreren, maar nieuwe stores in Pinia schrijven is verstandig.
Pinia installeren en registreren
Installeer eerst het pakket, maak daarna een instantie met createPinia() en registreer die op je app:
npm install pinia
// main.js
import { createApp } from "vue";
import { createPinia } from "pinia";
import App from "./App.vue";
const app = createApp(App);
app.use(createPinia());
app.mount("#app");
Dat is alles. Je kunt nu overal in de app stores definiëren en gebruiken. In plaats van het ene gigantische store-object van Vuex maak je met Pinia meerdere kleine stores.
Je eerste store: state, getters en actions
Een store wordt gedefinieerd met defineStore. Het eerste argument is een unieke id (devtools gebruikt die), het tweede is de inhoud van de store. Hier het klassieke teller-voorbeeld:
// stores/counter.js
import { defineStore } from "pinia";
export const useCounterStore = defineStore("counter", {
state: () => ({
count: 0,
name: "Teller",
}),
getters: {
double: (state) => state.count * 2,
},
actions: {
increment() {
this.count++;
},
reset() {
this.count = 0;
},
},
});
Je ziet de drie kernonderdelen:
- state: een functie die de begindata teruggeeft. Doordat het een functie is, krijgt elke instantie zijn eigen verse state.
- getters: berekende waarden afgeleid van de state. Ze worden gecachet zoals Vue's
computed; hier wordtdoubleniet opnieuw berekend tenzijcountverandert. - actions: methodes die de state wijzigen en je businesslogica bevatten. Anders dan in Vuex is er geen sync/async-splitsing; een action kan direct
asynczijn.
Merk op dat er geen mutations-sectie is zoals in Vuex. Wil je de state wijzigen, dan schrijf je gewoon this.count++ binnen een action.
De store gebruiken in een component
Om een store te gebruiken roep je hem aan binnen setup. Het teruggegeven object is reactief, dus je kunt het meteen in het template gebruiken:
<script setup>
import { useCounterStore } from "@/stores/counter";
const counter = useCounterStore();
</script>
<template>
<p>Teller: {{ counter.count }}</p>
<p>Dubbel: {{ counter.double }}</p>
<button @click="counter.increment()">Verhogen</button>
<button @click="counter.reset()">Resetten</button>
</template>
Hier zijn counter.count, counter.double en counter.increment() direct toegankelijk. Je leest de state in het template en triggert een action met een klik.
storeToRefs: destructureren zonder reactiviteit te verliezen
Vaak wil je een paar velden uit de store halen en met korte namen gebruiken. Maar als je de store direct destructureert, gaat de reactiviteit verloren:
// FOUT — count is niet meer reactief
const { count, double } = useCounterStore();
De juiste manier is storeToRefs gebruiken voor state en getters. Deze helper geeft elk veld terug als een reactieve ref. Actions, die functies zijn, kun je wel direct destructureren:
import { storeToRefs } from "pinia";
const store = useCounterStore();
const { count, double } = storeToRefs(store); // blijft reactief
const { increment, reset } = store; // actions zoals ze zijn
Asynchrone actions en gebundelde updates
In echte apps doen actions vaak API-aanroepen. Anders dan bij Vuex heb je geen aparte constructie nodig; maak de action gewoon async:
actions: {
async fetchUser(id) {
this.loading = true;
try {
const res = await fetch(`/api/users/${id}`);
this.user = await res.json();
} finally {
this.loading = false;
}
},
}
Wil je meerdere velden in één keer bijwerken, dan kun je $patch gebruiken; dit wordt als één wijziging bijgehouden en oogt netter in devtools:
store.$patch({ count: 10, name: "Nieuw" });
Je kunt de state ook terugzetten naar de beginwaarde met store.$reset(), en elke wijziging beluisteren met store.$subscribe() (bijvoorbeeld om naar localStorage te schrijven). Dit zijn kleine maar waardevolle details die laten zien hoeveel Pinia het dagelijkse werk vereenvoudigt vergeleken met Vuex.
Veelgestelde vragen
Moet ik Pinia of Vuex gebruiken?
Pinia voor nieuwe Vue 3-projecten. De officiële aanbeveling van Vue is nu Pinia, en Vuex staat in onderhoudsmodus. Heb je een groot bestaand Vuex-project, dan is dringende migratie niet nodig, maar nieuwe functies in Pinia schrijven is de meest toekomstbestendige keuze.
Waarom zijn er geen mutations in Pinia?
Vuex verplichtte mutations zodat devtools wijzigingen kon volgen. Omdat Pinia statewijzigingen direct kan volgen, werd die tussenlaag overbodig; je wijzigt de state binnen een action of met $patch, en devtools registreert nog steeds elke stap.
Kunnen meerdere stores met elkaar communiceren?
Ja. Binnen de action of getter van een store kun je een andere store aanroepen: je roept de betreffende useOtherStore()-functie aan en gebruikt de teruggegeven instantie. Stores kunnen elkaar bereiken zonder de module-namespace-complexiteit van Vuex.
Een goed opgezette state-architectuur is de ruggengraat van een groeiende Vue-app. Bouw je een Vue 3 + Pinia-project of wil je bestaande Vuex-code moderniseren, neem dan contact met me op — laten we samen een nette, schaalbare structuur opzetten.