Cours Vue.js · Module 11: Pinia
Le State dans Pinia
Dans cette leçon5
Le state, ce sont les données réactives stockées dans le store - comme la base de données de l'ordinateur principal au NOVA LAB. Chaque relevé de capteur, chaque paramètre de mission y est enregistré et disponible pour tous les modules de la station.
Définir le state
En style Composition API, le state se résume à ref() et reactive() :
1import { defineStore } from 'pinia'
2import { ref, reactive } from 'vue'
3
4export const useResourceStore = defineStore('resources', () => {
5 // Valeurs simples - ref()
6 const oxygen = ref(98.5)
7 const water = ref(1250)
8 const energy = ref(87)
9 const isEmergency = ref(false)
10
11 // Objets - reactive() ou ref()
12 const crew = reactive({
13 commander: 'Nova',
14 engineer: 'Atlas',
15 scientist: 'Darwin'
16 })
17
18 // Tableaux
19 const logs = ref([])
20
21 return { oxygen, water, energy, isEmergency, crew, logs }
22})Modifier le state
Le state peut être modifié directement dans les actions ou depuis un composant :
1export const useResourceStore = defineStore('resources', () => {
2 const oxygen = ref(98.5)
3
4 // L'action modifie le state
5 function consumeOxygen(amount) {
6 oxygen.value = Math.max(0, oxygen.value - amount)
7 }
8
9 return { oxygen, consumeOxygen }
10})1<script setup>
2const store = useResourceStore()
3
4// Modification directe (ça marche, mais les actions sont préférables)
5store.oxygen = 95.0
6
7// Via une action (recommandé)
8store.consumeOxygen(3.5)
9</script>$patch - mettre à jour plusieurs propriétés
$patch permet de mettre à jour plusieurs propriétés du state en une seule fois - plus efficace que de les modifier une par une :
1const store = useResourceStore()
2
3// Patch par objet - simple remplacement
4store.$patch({
5 oxygen: 95.0,
6 water: 1200,
7 energy: 82,
8 isEmergency: false
9})
10
11// Patch par fonction - pour les opérations complexes
12store.$patch((state) => {
13 state.oxygen -= 3.5
14 state.logs.push({
15 type: 'consumption',
16 timestamp: Date.now()
17 })
18})$reset - restaurer l'état initial
En style Options API, $reset() fonctionne automatiquement. En Composition API, tu dois définir ta propre fonction de reset :
1export const useResourceStore = defineStore('resources', () => {
2 const oxygen = ref(100)
3 const water = ref(1500)
4 const energy = ref(100)
5
6 // Reset personnalisé pour la Composition API
7 function $reset() {
8 oxygen.value = 100
9 water.value = 1500
10 energy.value = 100
11 }
12
13 return { oxygen, water, energy, $reset }
14})
15
16// Utilisation
17const store = useResourceStore()
18store.$reset() // Restaure les valeurs initiales$state - accéder à l'état complet
1const store = useResourceStore()
2
3// Lire tout le state
4const snapshot = store.$state
5
6// Remplacer tout le state
7store.$state = {
8 oxygen: 100,
9 water: 1500,
10 energy: 100,
11 isEmergency: false,
12 crew: { commander: 'Nova' },
13 logs: []
14}Code de cette leçon : App.vue
1<script setup>
2import { ref, computed } from 'vue'
3
4// Simulation d'un Resource Store
5const oxygen = ref(98.5)
6const water = ref(1250)
7const energy = ref(87)
8const logs = ref([])
9
10// Getters
11const oxygenStatus = computed(() => oxygen.value > 50 ? 'OK' : 'LOW')
12const waterStatus = computed(() => water.value > 500 ? 'OK' : 'LOW')
13const energyStatus = computed(() => energy.value > 40 ? 'OK' : 'LOW')
14
15// Actions
16function consumeOxygen(amount) {
17 oxygen.value = Math.max(0, +(oxygen.value - amount).toFixed(1))
18 addLog('Oxygen consumed: -' + amount + '%')
19}
20
21function consumeWater(amount) {
22 water.value = Math.max(0, water.value - amount)
23 addLog('Water consumed: -' + amount + 'L')
24}
25
26function consumeEnergy(amount) {
27 energy.value = Math.max(0, energy.value - amount)
28 addLog('Energy consumed: -' + amount + '%')
29}
30
31function addLog(message) {
32 logs.value.unshift({
33 message,
34 time: new Date().toLocaleTimeString()
35 })
36 if (logs.value.length > 8) logs.value.pop()
37}
38
39// Simulation de $patch
40function patchResources(updates) {
41 if (updates.oxygen !== undefined) oxygen.value = updates.oxygen
42 if (updates.water !== undefined) water.value = updates.water
43 if (updates.energy !== undefined) energy.value = updates.energy
44 addLog('Resources patched')
45}
46
47function resetResources() {
48 oxygen.value = 100
49 water.value = 1500
50 energy.value = 100
51 logs.value = []
52}
53</script>
54
55<template>
56 <div class="nova-lab">
57 <h1>NOVA LAB - State Management</h1>
58
59 <div class="grid">
60 <div class="resource-panel">
61 <h2>Resources</h2>
62 <div class="resource" v-for="r in [
63 { name: 'Oxygen', value: oxygen, unit: '%', status: oxygenStatus, action: () => consumeOxygen(5) },
64 { name: 'Water', value: water, unit: 'L', status: waterStatus, action: () => consumeWater(50) },
65 { name: 'Energy', value: energy, unit: '%', status: energyStatus, action: () => consumeEnergy(10) }
66 ]" :key="r.name">
67 <div class="r-header">
68 <span>{{ r.name }}</span>
69 <span :class="r.status.toLowerCase()">{{ r.status }}</span>
70 </div>
71 <div class="r-value">{{ r.value }}{{ r.unit }}</div>
72 <button @click="r.action" class="btn-sm">Consume</button>
73 </div>
74
75 <div class="btn-group">
76 <button @click="patchResources({ oxygen: 50, water: 800 })" class="btn">
77 $patch (O2=50, H2O=800)
78 </button>
79 <button @click="resetResources" class="btn btn-reset">
80 $reset
81 </button>
82 </div>
83 </div>
84
85 <div class="log-panel">
86 <h2>Activity Log</h2>
87 <div v-for="(log, i) in logs" :key="i" class="log-entry">
88 <span class="log-time">{{ log.time }}</span>
89 {{ log.message }}
90 </div>
91 <p v-if="!logs.length" class="empty">No activity yet...</p>
92 </div>
93 </div>
94 </div>
95</template>
96
97<style scoped>
98.nova-lab {
99 background: #0a0e27;
100 color: #00ff88;
101 min-height: 100vh;
102 padding: 20px;
103 font-family: monospace;
104}
105h1 { color: #00ff88; margin-bottom: 20px; text-align: center; }
106h2 { color: #00b4d8; margin-bottom: 15px; }
107.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; max-width: 700px; margin: 0 auto; }
108.resource-panel, .log-panel {
109 background: rgba(0, 180, 216, 0.1);
110 border: 2px solid #00b4d8;
111 padding: 15px;
112}
113.resource {
114 background: rgba(0, 255, 136, 0.05);
115 border: 1px solid #00ff88;
116 padding: 10px;
117 margin-bottom: 8px;
118}
119.r-header { display: flex; justify-content: space-between; margin-bottom: 5px; }
120.r-value { font-size: 1.2em; font-weight: bold; margin-bottom: 5px; }
121.ok { color: #00ff88; }
122.low { color: #ff4444; }
123.btn-sm {
124 background: #00b4d8;
125 color: #0a0e27;
126 border: none;
127 padding: 4px 12px;
128 cursor: pointer;
129 font-family: monospace;
130}
131.btn-group { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
132.btn {
133 background: #00b4d8;
134 color: #0a0e27;
135 border: none;
136 padding: 8px 12px;
137 cursor: pointer;
138 font-family: monospace;
139 font-size: 0.85em;
140}
141.btn:hover { background: #00ff88; }
142.btn-reset { background: #ff9900; }
143.log-entry {
144 padding: 5px 0;
145 border-bottom: 1px solid rgba(0, 180, 216, 0.3);
146 font-size: 0.85em;
147}
148.log-time { color: #00b4d8; margin-right: 8px; }
149.empty { color: #666; font-style: italic; }
150</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. Comment définit-on le state dans un store Pinia (Options API) ?
2. Que fait la méthode $patch dans Pinia ?
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
Entraîne-toi à travailler avec le state et la méthode $patch
- Tri vertical
Ordonne les étapes de configuration de Pinia dans une application Vue
- Clique dans l'ordre
Ordonne les éléments pour utiliser correctement storeToRefs
- Éditeur de code
Entraîne-toi à utiliser $patch avec une fonction de rappel