Pinia remplace vuex pour la création de stores. Ceux-ci permettent de gérer et persister l’état de différents objets afin qu’ils soient accessibles depuis n’importe quel composant de votre application. Dans ce petit tutoriel nous allons voir comment installer Pinia, créer un premier store et l’utiliser dans nos composants Vue.JS. Notez que pour cela nous avons utilisé Vue 3 et TypeScript.
Installation
Pour installer Pinia, placez-vous à la racine de votre projet (où se trouve le fichier package.json) et lancez la commande suivante:
npm install pinia
Ensuite, dans le fichier où vous montez l’application, vous devez instancier le store racine.
Exemple – main.ts :
import { createApp } from 'vue'
import { createPinia } from 'pinia'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia).mount('#app')
Pour ce faire on a utilisé la méthode createPinia() puis on a lié le store à l’application à l’aide de la méthode use().
Créer la structure
Il faut maintenant créer la structure dans notre projet. Pour cela nous allons ajouter un sous-dossier stores au dossier « src« . Chaque store se trouvera dans son propre fichier. Il n’est plus nécessaire de créer un fichier index où vous instanciez chacun d’entre eux.
Créer votre premier store
Nous allons maintenant créer un fichier dans le dossier que l’on vient de créer, que nous appellerons user.ts. Il permettra de partager les informations de l’utilisateur connecté entre les différents composants et les différentes vues de notre application.
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: (): IUserState => ({
userId: null,
name: null,
email: null
}),
actions: {
initUser(id: number) {
this.userId = 123
this.name = 'test'
this.email = 'xxx@gmail.com'
},
},
getters: {
getUserName(state: IAuthState): string|null {
return state.name
},
},
})
interface IUserState {
userId: null | string,
name: null | string,
email: null | string
}
Pour créer le store nous utilisons la méthode defineStore() qui prend en paramètres un nom ainsi qu’un ensemble d’options. Parmi celles-ci, on distingue l’état, les actions ainsi que les getters. Vous remarquerez que contrairement à vuex il n’y a plus de notion de mutation.
Notez que les propriétés sont directement accessibles sans passer par un getter. On peut directement les modifier et y accéder sans passer par une action, ce qui peut être très pratique dans le cas de simples booléens à modifier.
Autre petite remarque qui peut avoir son importance : on ne peut pas utiliser le mot-clé « this » dans les fonctions fléchées (arrow functions)
Comment y accéder au sein de composants?
En fonction de l’API utilisée (Options ou Composition), voici comment on accède et utilise un store au sein de nos composants.
Composition API
script
<script setup lang="ts">
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
</script>
template
<div id="my-component">
<p>Test</p>
<div v-if="userStore.getUserName">
<p>User name is: {{ userStore.getUserName }}</p>
</div>
</div>
Options API
script
<script lang="ts">
import { computed, defineComponent } from 'vue'
import { useUserStore } from '@/stores/user'
export default defineComponent({
name: 'MyComponent',
components: {
// ...
},
setup() {
const userStore = useUserStore()
const userName = computed(() => userStore.getUserName)
return {
userName
};
},
});
</script>
template
<div id="my-component">
<p>Test</p>
<div v-if="userName">
<p>User name is : {{ userName }}</p>
</div>
</div>
Setup Stores
Dans le cas d’une utilisation plus poussée, par exemple pour surveiller une propriété qui serait modifiée (via ce qu’on appelle des watchers), il faut utiliser les “setup stores”.
Exemple – user.ts :
import { ref } from 'vue'
import { defineStore } from 'pinia'
import UserService from "@/services/UserService"
import type { User } from '@/types/User'
export const useUserStore = defineStore('user', () => {
const user = ref<User|null>(null);
async function initUser(id: number): Promise<void>
{
const response = await UserService.getUser(id);
user.value = response;
}
// Override $reset function for option store
function $reset()
{
user.value = null;
}
return {
initUser,
user,
$reset
}
})
Voici la différence entre les deux types de stores:
Pinia Setup Store | Classic Pinia Store |
---|---|
|
|
| Getter |
| Action |
Dans ce type de store il faut obligatoirement redéfinir la méthode $reset
.
Utiliser la persistance
Pour permettre d’utiliser le stockage de données en local, on peut ajouter un plugin comme avec vuex.
Le plugin est disponible sur GitHub.
Ajouter le plugin en lançant la commande suivante à la racine du projet :
npm install pinia-plugin-persistedstate
Dans le fichier du point d’entrée de l’application on modifie le code comme ceci :
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
const app = createApp(App)
pinia.use(piniaPluginPersistedstate)
app.use(pinia).mount('#app')
Ensuite dans la définition du store, dans les options on rajoute le booléen persist à true.
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
// Options...
},
persist: true
})
On peut définir plus précisément le stockage pour chaque élément à conserver. Si on ne spécifie rien, par défaut le stockage utilisé est localStorage. On remplace alors le booléen par un tableau d’objets où on définit chaque fois 2 éléments, paths et storage :
persist: [
{
paths: ['user'],
storage: localStorage,
},
{
paths: ['token'],
storage: cookiesStorage,
},
],
Dans l’exemple ci-dessus, cookiesStorage est un objet créé spécifiquement pour notre store et n’existe pas par défaut. On peut cependant utiliser localStorage ou sessionStorage.
Ici, on spécifie explicitement que l’objet “user” sera enregistré dans le stockage local du navigateur, et que le token sera stocké dans un cookie. On peut également spécifier le stockage plus globalement lors de la création du store.
Vous avez désormais toutes les bases nécessaires pour créer un store avec Pinia dans votre application Vue.JS. Bon développement !