Cours Vue.js · Module 11: Pinia
Les bases de Pinia
Dans cette leçon5
Bienvenue dans la base de données centrale du NOVA LAB ! Année 2087 - c'est ici que nous stockons toutes les données critiques de la mission martienne. Tout comme l'ordinateur principal de la base conserve les informations sur les systèmes, l'équipage et les ressources, Pinia stocke l'état global d'une application Vue.
Qu'est-ce que Pinia ?
Pinia est la bibliothèque officielle de gestion d'état pour Vue 3. C'est le successeur de Vuex et il propose une syntaxe bien plus simple. Considère-le comme l'ordinateur central du NOVA LAB - un point de contrôle unique d'où tous les modules de la station récupèrent leurs données.
Principaux avantages de Pinia :
- Syntaxe plus simple que Vuex (pas de mutations !)
- Support complet de TypeScript
- Excellente intégration avec les Vue DevTools
- Architecture modulaire - plusieurs stores indépendants
Installation et configuration
Pour démarrer Pinia, il te faut deux étapes - installer le paquet et le connecter à l'application :
1npm install pinia1// main.js
2import { createApp } from 'vue'
3import { createPinia } from 'pinia'
4import App from './App.vue'
5
6const pinia = createPinia()
7const app = createApp(App)
8
9app.use(pinia)
10app.mount('#app')La fonction createPinia() crée une instance de Pinia, et app.use(pinia) l'enregistre en tant que plugin Vue. À partir de ce moment, tous les stores sont disponibles dans toute l'application.
Définir un store - defineStore
Un store se crée avec defineStore. Il prend deux arguments : un nom de store unique et une fonction setup (style Composition API) :
1// stores/mission.js
2import { defineStore } from 'pinia'
3import { ref, computed } from 'vue'
4
5export const useMissionStore = defineStore('mission', () => {
6 // State - données réactives
7 const missionName = ref('Mars Exploration Alpha')
8 const status = ref('active')
9 const crewCount = ref(6)
10
11 // Getters - valeurs calculées
12 const statusLabel = computed(() =>
13 status.value === 'active' ? 'ACTIVE' : 'INACTIVE'
14 )
15
16 // Actions - fonctions qui modifient l'état
17 function updateStatus(newStatus) {
18 status.value = newStatus
19 }
20
21 // On retourne tout ce qui doit être accessible
22 return {
23 missionName,
24 status,
25 crewCount,
26 statusLabel,
27 updateStatus
28 }
29})Convention de nommage : useXxxStore - préfixe use et suffixe Store.
Utiliser le store dans un composant
1<script setup>
2import { useMissionStore } from '@/stores/mission'
3
4const missionStore = useMissionStore()
5
6// Accès à l'état
7console.log(missionStore.missionName)
8
9// Appeler une action
10missionStore.updateStatus('completed')
11</script>
12
13<template>
14 <div>
15 <h1>{{ missionStore.missionName }}</h1>
16 <p>Status: {{ missionStore.statusLabel }}</p>
17 <button @click="missionStore.updateStatus('paused')">
18 Pause Mission
19 </button>
20 </div>
21</template>storeToRefs - déstructuration réactive
La déstructuration classique fait perdre la réactivité. Utilise storeToRefs pour l'état et les getters :
1<script setup>
2import { useMissionStore } from '@/stores/mission'
3import { storeToRefs } from 'pinia'
4
5const store = useMissionStore()
6
7// State et getters - storeToRefs (préserve la réactivité)
8const { missionName, status, statusLabel } = storeToRefs(store)
9
10// Actions - déstructuration classique (ce sont des fonctions, pas des refs)
11const { updateStatus } = store
12</script>Code de cette leçon : App.vue
1<script setup>
2import { ref, computed } from 'vue'
3
4// Simulation d'un store Pinia (style Composition API)
5// Dans une vraie application : import { defineStore } from 'pinia'
6
7// State
8const missionName = ref('Mars Exploration Alpha')
9const status = ref('active')
10const crewCount = ref(6)
11const fuelLevel = ref(87)
12
13// Getters
14const statusLabel = computed(() =>
15 status.value === 'active' ? 'ACTIVE' : 'INACTIVE'
16)
17
18const fuelStatus = computed(() => {
19 if (fuelLevel.value > 70) return 'Optimal'
20 if (fuelLevel.value > 30) return 'Moderate'
21 return 'Critical'
22})
23
24// Actions
25function toggleStatus() {
26 status.value = status.value === 'active' ? 'paused' : 'active'
27}
28
29function consumeFuel(amount) {
30 fuelLevel.value = Math.max(0, fuelLevel.value - amount)
31}
32
33function refuel() {
34 fuelLevel.value = 100
35}
36</script>
37
38<template>
39 <div class="nova-lab">
40 <h1>NOVA LAB - Pinia Basics</h1>
41
42 <div class="panel">
43 <h2>Mission Store</h2>
44 <div class="stat">
45 <span class="label">Mission:</span>
46 <span>{{ missionName }}</span>
47 </div>
48 <div class="stat">
49 <span class="label">Status:</span>
50 <span :class="status">{{ statusLabel }}</span>
51 </div>
52 <div class="stat">
53 <span class="label">Crew:</span>
54 <span>{{ crewCount }} members</span>
55 </div>
56 <div class="stat">
57 <span class="label">Fuel:</span>
58 <div class="fuel-bar">
59 <div class="fuel-fill" :style="{ width: fuelLevel + '%' }" :class="fuelStatus.toLowerCase()"></div>
60 </div>
61 <span>{{ fuelLevel }}% ({{ fuelStatus }})</span>
62 </div>
63
64 <div class="actions">
65 <button @click="toggleStatus" class="btn">
66 {{ status === 'active' ? 'Pause' : 'Resume' }}
67 </button>
68 <button @click="consumeFuel(10)" class="btn btn-warn">
69 Consume Fuel (-10)
70 </button>
71 <button @click="refuel" class="btn btn-success">
72 Refuel
73 </button>
74 </div>
75 </div>
76 </div>
77</template>
78
79<style scoped>
80.nova-lab {
81 background: #0a0e27;
82 color: #00ff88;
83 min-height: 100vh;
84 padding: 20px;
85 font-family: monospace;
86}
87h1 { color: #00ff88; margin-bottom: 20px; text-align: center; }
88.panel {
89 background: rgba(0, 180, 216, 0.1);
90 border: 2px solid #00b4d8;
91 padding: 20px;
92 max-width: 500px;
93 margin: 0 auto;
94}
95h2 { color: #00b4d8; margin-bottom: 15px; }
96.stat {
97 display: flex;
98 align-items: center;
99 gap: 10px;
100 margin: 10px 0;
101 flex-wrap: wrap;
102}
103.label { color: #00b4d8; min-width: 80px; }
104.active { color: #00ff88; font-weight: bold; }
105.paused { color: #ff9900; font-weight: bold; }
106.fuel-bar {
107 width: 150px;
108 height: 16px;
109 background: #1a1e3f;
110 border: 1px solid #00b4d8;
111}
112.fuel-fill {
113 height: 100%;
114 transition: width 0.3s;
115}
116.fuel-fill.optimal { background: #00ff88; }
117.fuel-fill.moderate { background: #ff9900; }
118.fuel-fill.critical { background: #ff4444; }
119.actions {
120 display: flex;
121 gap: 8px;
122 margin-top: 15px;
123 flex-wrap: wrap;
124}
125.btn {
126 background: #00b4d8;
127 color: #0a0e27;
128 border: none;
129 padding: 8px 16px;
130 cursor: pointer;
131 font-family: monospace;
132 font-weight: bold;
133}
134.btn:hover { background: #00ff88; }
135.btn-warn { background: #ff9900; }
136.btn-warn:hover { background: #ffbb33; }
137.btn-success { background: #00ff88; }
138.btn-success:hover { background: #33ffaa; }
139</style>Tu as repéré une erreur dans cette leçon ?
Teste tes connaissances
Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.
1. Qu'est-ce que Pinia dans l'écosystème Vue 3 ?
2. Comment crée-t-on une instance de Pinia dans main.js ?
Voici 2 des 3 questions de cette leçon. Réponds aux autres dans le jeu.
Tâches pratiques dans le jeu
- Éditeur de code
Crée ton premier store Pinia avec state, getters et actions
- Clique dans l'ordre
Ordonne les éléments pour appeler correctement defineStore
- Éditeur de code
Utilise un store Pinia dans un composant Vue