vue pinia est la façon officielle, et aujourd'hui recommandée, de gérer l'état partagé dans les applications Vue 3. Lorsque plusieurs composants ont besoin des mêmes données — l'utilisateur connecté, le panier, la préférence de thème — tu veux les garder à un seul endroit central. L'ancien standard pour cela était Vuex, mais l'équipe Vue recommande désormais Pinia pour les nouveaux projets. Dans cet article, nous allons voir pas à pas comment construire un store de zéro avec Pinia plutôt qu'avec Vuex.
Pourquoi Pinia plutôt que Vuex ?
Pinia est la bibliothèque officielle de gestion d'état qui remplace Vuex, et elle est préférée parce qu'elle résout plusieurs problèmes concrets :
- Moins de cérémonie : dans Vuex, il fallait écrire des
mutationspour modifier quoi que ce soit. Pinia supprime totalement la notion de mutation ; tu modifies l'état directement ou depuis lesactions. - Support TypeScript complet : les types sont déduits automatiquement ;
thiset les valeurs de retour sont typés sans code répétitif. - Pas de modules, des stores plats : au lieu de la structure imbriquée de Vuex, chaque store est une unité indépendante. Tu appelles n'importe quel store depuis n'importe où.
- Conçu pour la Composition API : Pinia fonctionne naturellement avec la logique
setupde Vue 3 et offre le support des devtools.
En bref, Pinia fait tout ce que faisait Vuex avec moins de code et une meilleure sécurité de typage. Si tu as déjà un projet Vuex, tu n'es pas obligé de migrer de force, mais écrire les nouveaux stores avec Pinia est judicieux.
Installer et enregistrer Pinia
Installe d'abord le paquet, puis crée une instance avec createPinia() et enregistre-la sur ton application :
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");
C'est tout. Tu peux maintenant définir et utiliser des stores partout dans l'application. À la place de l'unique objet store géant de Vuex, avec Pinia tu crées plusieurs petits stores.
Ton premier store : state, getters et actions
Un store se définit avec defineStore. Le premier argument est un identifiant unique (utilisé par les devtools), le second est le contenu du store. Voici l'exemple classique du compteur :
// stores/counter.js
import { defineStore } from "pinia";
export const useCounterStore = defineStore("counter", {
state: () => ({
count: 0,
name: "Compteur",
}),
getters: {
double: (state) => state.count * 2,
},
actions: {
increment() {
this.count++;
},
reset() {
this.count = 0;
},
},
});
On distingue les trois éléments centraux :
- state : une fonction qui renvoie les données initiales. Le fait que ce soit une fonction garantit que chaque instance reçoit son propre état neuf.
- getters : des valeurs calculées dérivées de l'état. Elles sont mises en cache comme le
computedde Vue ; icidoublen'est pas recalculé tant quecountne change pas. - actions : des méthodes qui modifient l'état et contiennent ta logique métier. Contrairement à Vuex, il n'y a pas de séparation synchrone/asynchrone ; une action peut être
asyncdirectement.
Remarque qu'il n'y a aucune section mutations comme dans Vuex. Quand tu veux modifier l'état, tu écris simplement this.count++ dans une action.
Utiliser le store dans un composant
Pour utiliser un store, tu l'appelles dans setup. L'objet retourné est réactif, donc tu peux l'utiliser directement dans le template :
<script setup>
import { useCounterStore } from "@/stores/counter";
const counter = useCounterStore();
</script>
<template>
<p>Compteur : {{ counter.count }}</p>
<p>Double : {{ counter.double }}</p>
<button @click="counter.increment()">Incrémenter</button>
<button @click="counter.reset()">Réinitialiser</button>
</template>
Ici counter.count, counter.double et counter.increment() sont directement accessibles. Tu lis l'état dans le template et tu déclenches une action d'un clic.
storeToRefs : déstructurer sans perdre la réactivité
Souvent tu veux extraire quelques champs du store et les utiliser avec des noms courts. Mais si tu déstructures le store directement, la réactivité est perdue :
// FAUX — count n'est plus réactif
const { count, double } = useCounterStore();
La bonne méthode est d'utiliser storeToRefs pour le state et les getters. Cet utilitaire renvoie chaque champ sous forme de ref réactive. Les actions, étant des fonctions, peuvent être déstructurées directement :
import { storeToRefs } from "pinia";
const store = useCounterStore();
const { count, double } = storeToRefs(store); // reste réactif
const { increment, reset } = store; // actions prises telles quelles
Actions asynchrones et mises à jour groupées
Dans les vraies applications, les actions font souvent des appels API. Contrairement à Vuex, tu n'as pas besoin d'une construction séparée ; il suffit de rendre l'action 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;
}
},
}
Si tu veux mettre à jour plusieurs champs d'un coup, tu peux utiliser $patch ; c'est suivi comme un seul changement et c'est plus propre dans les devtools :
store.$patch({ count: 10, name: "Nouveau" });
Tu peux aussi réinitialiser l'état à sa valeur initiale avec store.$reset(), et écouter chaque changement avec store.$subscribe() (par exemple pour écrire dans le localStorage). Ce sont de petits détails précieux qui montrent à quel point Pinia simplifie le travail quotidien par rapport à Vuex.
Questions fréquentes
Dois-je utiliser Pinia ou Vuex ?
Pinia pour les nouveaux projets Vue 3. La recommandation officielle de Vue est désormais Pinia, et Vuex est en mode maintenance. Si tu as un gros projet Vuex existant, une migration urgente n'est pas nécessaire, mais écrire les nouvelles fonctionnalités avec Pinia est le choix le plus pérenne.
Pourquoi n'y a-t-il pas de mutations dans Pinia ?
Vuex imposait les mutations pour que les devtools puissent suivre les changements. Comme Pinia peut suivre directement les changements d'état, cette couche intermédiaire est devenue inutile ; tu modifies l'état dans une action ou avec $patch, et les devtools enregistrent quand même chaque étape.
Plusieurs stores peuvent-ils communiquer entre eux ?
Oui. Dans l'action ou le getter d'un store, tu peux appeler un autre store : tu invoques la fonction useOtherStore() correspondante et tu utilises l'instance retournée. Les stores peuvent s'atteindre sans la complexité des namespaces de modules de Vuex.
Une architecture d'état bien structurée est la colonne vertébrale d'une application Vue qui grandit. Si tu construis un projet Vue 3 + Pinia ou que tu veux moderniser du code Vuex existant, contacte-moi — mettons en place ensemble une structure propre et évolutive.