Kurs Vue.js · Moduł 11: Pinia

State w Pinia

2 min czytania
W tej lekcji5

State to reaktywne dane przechowywane w store - jak baza danych głównego komputera NOVA LAB. Każdy odczyt sensora, każdy parametr misji jest tu zapisany i dostępny dla wszystkich modułów stacji.

Definiowanie state

W Composition API style state to zwykłe ref() i reactive():

1import { defineStore } from 'pinia'
2import { ref, reactive } from 'vue'
3
4export const useResourceStore = defineStore('resources', () => {
5  // Proste wartości - ref()
6  const oxygen = ref(98.5)
7  const water = ref(1250)
8  const energy = ref(87)
9  const isEmergency = ref(false)
10
11  // Obiekty - reactive() lub ref()
12  const crew = reactive({
13    commander: 'Nova',
14    engineer: 'Atlas',
15    scientist: 'Darwin'
16  })
17
18  // Tablice
19  const logs = ref([])
20
21  return { oxygen, water, energy, isEmergency, crew, logs }
22})

Modyfikacja state

State można modyfikować bezpośrednio w actions lub z komponentu:

1export const useResourceStore = defineStore('resources', () => {
2  const oxygen = ref(98.5)
3
4  // Action modyfikuje 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// Bezpośrednia modyfikacja (działa, ale actions są lepsze)
5store.oxygen = 95.0
6
7// Przez action (zalecane)
8store.consumeOxygen(3.5)
9</script>

$patch - aktualizacja wielu właściwości

$patch pozwala zaktualizować wiele właściwości state jednocześnie - to bardziej wydajne niż modyfikacja po jednej:

1const store = useResourceStore()
2
3// Obiekt patch - proste nadpisanie
4store.$patch({
5  oxygen: 95.0,
6  water: 1200,
7  energy: 82,
8  isEmergency: false
9})
10
11// Funkcja patch - dla złożonych operacji
12store.$patch((state) => {
13  state.oxygen -= 3.5
14  state.logs.push({
15    type: 'consumption',
16    timestamp: Date.now()
17  })
18})

$reset - przywracanie stanu początkowego

W Options API style $reset() działa automatycznie. W Composition API musisz zdefiniować własną funkcję reset:

1export const useResourceStore = defineStore('resources', () => {
2  const oxygen = ref(100)
3  const water = ref(1500)
4  const energy = ref(100)
5
6  // Custom reset dla 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// Użycie
17const store = useResourceStore()
18store.$reset() // Przywraca wartości początkowe

$state - dostęp do całego stanu

1const store = useResourceStore()
2
3// Odczyt całego state
4const snapshot = store.$state
5
6// Nadpisanie całego state
7store.$state = {
8  oxygen: 100,
9  water: 1500,
10  energy: 100,
11  isEmergency: false,
12  crew: { commander: 'Nova' },
13  logs: []
14}
Kod do tej lekcji: App.vue
1<script setup>
2import { ref, computed } from 'vue'
3
4// Symulacja store zasobów
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('Zużyto tlen: -' + amount + '%')
19}
20
21function consumeWater(amount) {
22  water.value = Math.max(0, water.value - amount)
23  addLog('Zużyto wodę: -' + amount + ' L')
24}
25
26function consumeEnergy(amount) {
27  energy.value = Math.max(0, energy.value - amount)
28  addLog('Zużyto energię: -' + 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// Symulacja $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('Zasoby zaktualizowane przez $patch')
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 - zarządzanie stanem</h1>
58
59    <div class="grid">
60      <div class="resource-panel">
61        <h2>Zasoby</h2>
62        <div class="resource" v-for="r in [
63          { name: 'Tlen', value: oxygen, unit: '%', status: oxygenStatus, action: () => consumeOxygen(5) },
64          { name: 'Woda', value: water, unit: 'L', status: waterStatus, action: () => consumeWater(50) },
65          { name: 'Energia', 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">Zużyj</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>Dziennik aktywności</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">Brak aktywności...</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>

Widzisz błąd w tej lekcji?

Sprawdź się

Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.

  1. 1. Jak zdefiniować state w Composition API style?

  2. 2. Do czego służy $patch() w Pinia?

To 2 z 3 pytań do tej lekcji. Pozostałe rozwiążesz w grze.

Zadania praktyczne w grze

  • Edytor kodu

    Stwórz store z ref() state, action consumeOxygen i action patchResources.

  • Układanie w pionie

    Ułóż kroki konfiguracji Pinia w main.js:

  • Klikanie w kolejności

    Ułóż kod destrukturyzacji store z zachowaniem reaktywności:

  • Edytor kodu

    Stwórz patchUpdate (ustawia wartości) i patchFunction (zmniejsza o 10%).

Przydatne artykuły