Kurs Vue.js · Moduł 6: Komponenty
Lifecycle Hooks - Cykl Życia Komponentu
W tej lekcji6
Dr. Nova wyjaśnia: "Każdy moduł stacji NOVA LAB przechodzi przez fazy: instalację, uruchomienie, aktualizacje firmware'u i ostatecznie demontaż. Komponenty Vue mają identyczny cykl życia - i możesz wykonywać kod w każdej z tych faz!"
Panel modułu ma pobrać dane z serwera zaraz po pojawieniu się na ekranie, a po zamknięciu wyłączyć licznik czasu pracy. Kod w <script setup> wykonuje się jednak tylko raz, w chwili tworzenia komponentu, zanim na stronie powstanie jakikolwiek jego element. Potrzebujesz sposobu, żeby powiedzieć Vue: to uruchom, gdy panel już stoi, a to - gdy znika.
Fazy Cyklu Życia Komponentu
Komponent Vue przechodzi przez kilka kluczowych faz:
- Tworzenie (Creation) - komponent jest inicjalizowany, reaktywny stan jest konfigurowany
- Montowanie (Mounting) - komponent jest wstawiany do DOM (staje się widoczny na stronie)
- Aktualizacja (Updating) - reaktywne dane się zmieniają, DOM jest aktualizowany
- Odmontowanie (Unmounting) - komponent jest usuwany z DOM
W każdej fazie możesz uruchomić kod za pomocą lifecycle hooks - specjalnych funkcji wywoływanych automatycznie przez Vue.
Kod zapisany wprost w <script setup> to faza tworzenia: kompilator zamienia ten blok w funkcję setup(), którą Vue wywołuje raz, zanim zacznie montować komponent. Tam też rejestrujesz hooki - wywołujesz funkcję zaimportowaną z pakietu vue i przekazujesz jej własną funkcję, którą Vue uruchomi we właściwym momencie.
onBeforeMount i onMounted
onMounted to najczęściej używany hook. Uruchamia się po tym, jak komponent został wstawiony do DOM. Porównaj go z onBeforeMount w module statusu:
1<template>
2 <div class="module-status">
3 <p>Status: {{ status }}</p>
4 <p>Dane: {{ data }}</p>
5 </div>
6</template>
7
8<script setup>
9import { ref, onBeforeMount, onMounted } from 'vue'
10
11const status = ref('Inicjalizacja...')
12const data = ref(null)
13
14// Przed zamontowaniem - DOM jeszcze nie istnieje
15onBeforeMount(() => {
16 console.log('Komponent zaraz się zamontuje')
17 status.value = 'Montowanie...'
18})
19
20// Po zamontowaniu - DOM jest gotowy!
21onMounted(() => {
22 console.log('Komponent zamontowany w DOM')
23 status.value = 'Aktywny'
24
25 // Typowe zastosowanie: pobieranie danych z API
26 fetchModuleData()
27})
28
29async function fetchModuleData() {
30 const response = await fetch('/api/modules')
31 data.value = await response.json()
32}
33</script>Kolejność jest zawsze ta sama: najpierw kod setup, potem onBeforeMount, gdy elementy komponentu jeszcze nie powstały, a na końcu onMounted, gdy komponent stoi już w DOM. Jeśli komponent ma dzieci, jego onMounted wywoła się dopiero po zamontowaniu wszystkich dzieci. Funkcja fetchModuleData jest zadeklarowana niżej, ale deklaracje funkcji są w JavaScripcie dostępne w całym bloku, więc wywołanie działa.
Kiedy używać onMounted:
- Pobieranie danych z API (fetch data)
- Dostęp do elementów DOM (
document.querySelector, referencje) - Inicjalizacja bibliotek zewnętrznych (np. wykresy, mapy)
- Uruchamianie timerów i interwałów
Do elementów DOM sięga się w Vue zwykle przez template refs, które poznasz w Pracowni Hologramów - document.querySelector też działa, ale omija mechanizmy frameworka.
onBeforeUnmount i onUnmounted
Te hooki uruchamiają się, gdy komponent jest usuwany z DOM. Służą do czyszczenia zasobów, na przykład timera uruchomionego przy montowaniu:
1<script setup>
2import { ref, onMounted, onBeforeUnmount, onUnmounted } from 'vue'
3
4const timer = ref(null)
5const seconds = ref(0)
6
7onMounted(() => {
8 // Uruchamiamy timer po zamontowaniu
9 timer.value = setInterval(() => {
10 seconds.value++
11 }, 1000)
12 console.log('Timer uruchomiony')
13})
14
15onBeforeUnmount(() => {
16 // Przed odmontowaniem - komponent jeszcze jest w DOM
17 console.log('Komponent zaraz zostanie usunięty')
18})
19
20onUnmounted(() => {
21 // Czyścimy timer, żeby nie działał po usunięciu komponentu!
22 if (timer.value) {
23 clearInterval(timer.value)
24 console.log('Timer wyczyszczony')
25 }
26})
27</script>onBeforeUnmount działa, gdy komponent jest jeszcze na stronie, a onUnmounted - gdy jego elementów w DOM już nie ma. To drugie miejsce jest typowe dla sprzątania, bo komponent na pewno skończył pracę. Identyfikator timera trzymamy tu w ref(), choć reaktywność nie jest mu potrzebna - nie wyświetlamy go na ekranie.
Dlaczego czyszczenie jest ważne? Jeśli nie wyczyścisz setInterval, timer będzie działał nawet po usunięciu komponentu z DOM, powodując wycieki pamięci (memory leaks). Każde ponowne otwarcie panelu dokładałoby kolejny licznik, który zmienia dane, których nikt już nie ogląda.
onBeforeUpdate i onUpdated
Te hooki reagują na zmiany reaktywnych danych i aktualizację DOM, która po nich następuje:
1<script setup>
2import { ref, onBeforeUpdate, onUpdated } from 'vue'
3
4const count = ref(0)
5
6onBeforeUpdate(() => {
7 // Przed aktualizacją DOM
8 console.log('DOM zaraz się zaktualizuje')
9})
10
11onUpdated(() => {
12 // Po aktualizacji DOM
13 console.log('DOM zaktualizowany, nowa wartość:', count.value)
14})
15</script>onBeforeUpdate widzi jeszcze stary DOM, a onUpdated już nowy. Nie zmieniaj w onUpdated stanu komponentu, bo każda zmiana wywoła kolejne przerysowanie i grozi nieskończoną pętlą. Te hooki są rzadziej używane - przydają się gdy musisz zareagować na zmiany w DOM (np. scroll position, animacje).
Praktyczny Przykład: Moduł z Danymi i Timerem
Złóżmy najważniejsze hooki w module generatora tlenu. Szablon wyświetla nazwę, czas pracy, status i dziennik zdarzeń, który dyrektywa v-for zamienia w listę:
1<template>
2 <div class="station-module" style="background: #0a0e27; color: #00ff88; padding: 1rem;">
3 <h3>Moduł: {{ moduleName }}</h3>
4 <p>Czas pracy: {{ uptime }}s</p>
5 <p>Status: {{ status }}</p>
6 <ul>
7 <li v-for="log in logs" :key="log">{{ log }}</li>
8 </ul>
9 </div>
10</template>Całą pracę wykonuje skrypt: po zamontowaniu zapisuje wpisy w dzienniku i uruchamia licznik, a po odmontowaniu go zatrzymuje:
1<script setup>
2import { ref, onMounted, onUnmounted } from 'vue'
3
4const moduleName = ref('Oxygen Generator')
5const uptime = ref(0)
6const status = ref('Uruchamianie...')
7const logs = ref([])
8let intervalId = null
9
10onMounted(() => {
11 logs.value.push('System zamontowany')
12 status.value = 'Aktywny'
13
14 // Timer aktualizujący uptime co sekundę
15 intervalId = setInterval(() => {
16 uptime.value++
17 }, 1000)
18
19 logs.value.push('Timer uruchomiony')
20})
21
22onUnmounted(() => {
23 // Czyszczenie przy odmontowaniu
24 if (intervalId) {
25 clearInterval(intervalId)
26 }
27 console.log('Moduł wyłączony, zasoby zwolnione')
28})
29</script>Tym razem intervalId to zwykłe let. Wszystko, co widać na ekranie, siedzi w kontenerach ref(), a to, co tylko sprząta po module, w zwykłych zmiennych. Polecam Ci ten podział - reaktywność zakładaj tylko tam, gdzie coś ma się odświeżać.
Kolejność Hooków - Podsumowanie
Na koniec cały cykl w jednym miejscu, w kolejności, w jakiej Vue wywołuje poszczególne hooki:
1Tworzenie: setup() → onBeforeMount → onMounted
2Aktualizacja: onBeforeUpdate → onUpdated
3Odmontowanie: onBeforeUnmount → onUnmountedAktualizacja może się powtórzyć wiele razy albo wcale, a montowanie i odmontowanie zdarzają się raz na życie komponentu. Dodanie hooka zawsze wygląda tak samo: import z vue, wywołanie z funkcją, a kod w jej środku wykona się we właściwej fazie.
Zapamiętaj dwa najważniejsze:
onMounted- pobieranie danych, inicjalizacja (start)onUnmounted- czyszczenie timerów, listenerów (cleanup)
W edytorze poniżej przycisk montuje i odmontowuje komponent z licznikiem. Klikaj go i obserwuj w konsoli przeglądarki kolejność hooków.
Moduł stacji żyje tak jak komponent: co uruchomisz przy zapłonie w onMounted, to wyłącz przy demontażu w onUnmounted.
Kod do tej lekcji: App.vue
1<script setup>
2import { ref } from 'vue'
3import LifecycleDemo from './LifecycleDemo.vue'
4
5const showTimer = ref(true)
6</script>
7
8<template>
9 <div class="lifecycle-lab">
10 <div class="header">
11 <h1>Lifecycle Hooks - NOVA LAB</h1>
12 <p class="subtitle">Cykl zycia komponentu Vue</p>
13 </div>
14
15 <div class="info-panel">
16 <h3>Lifecycle Hooks</h3>
17 <p><strong>onBeforeMount</strong> - tuz przed zamontowaniem komponentu</p>
18 <p><strong>onMounted</strong> - komponent zamontowany, DOM dostepny</p>
19 <p><strong>onBeforeUnmount</strong> - tuz przed odmontowaniem</p>
20 <p><strong>onUnmounted</strong> - komponent odmontowany, sprzatanie zasobow</p>
21 </div>
22
23 <div class="toggle-section">
24 <button class="toggle-btn" @click="showTimer = !showTimer">
25 {{ showTimer ? 'Odmontuj komponent' : 'Zamontuj komponent' }}
26 </button>
27 <p class="hint">Kliknij przycisk i obserwuj logi w konsoli (F12)</p>
28 </div>
29
30 <LifecycleDemo v-if="showTimer" />
31
32 <div v-if="!showTimer" class="unmounted-msg">
33 <p>Komponent zostal odmontowany.</p>
34 <p>Sprawdz konsole - zobaczysz logi onBeforeUnmount i onUnmounted.</p>
35 <p>Timer zostal wyczyszczony dzieki cleanup w onUnmounted!</p>
36 </div>
37 </div>
38</template>
39
40<style scoped>
41.lifecycle-lab {
42 background: linear-gradient(135deg, #0a0e27 0%, #1a1e3f 100%);
43 min-height: 100vh;
44 padding: 2rem;
45 color: #ffffff;
46 font-family: 'Courier New', monospace;
47}
48
49.header {
50 text-align: center;
51 margin-bottom: 2rem;
52 padding-bottom: 1rem;
53 border-bottom: 2px solid #00ff88;
54}
55
56.header h1 {
57 font-size: 2rem;
58 color: #00ff88;
59 text-shadow: 0 0 20px rgba(0, 255, 136, 0.5);
60 margin: 0;
61}
62
63.subtitle {
64 color: #00b4d8;
65 font-size: 1.1rem;
66 margin-top: 0.5rem;
67}
68
69.info-panel {
70 background: rgba(0, 180, 216, 0.1);
71 border: 2px solid #00b4d8;
72 border-radius: 10px;
73 padding: 1.5rem;
74 margin-bottom: 2rem;
75}
76
77.info-panel h3 {
78 color: #00b4d8;
79 margin-top: 0;
80}
81
82.info-panel strong {
83 color: #00ff88;
84}
85
86.toggle-section {
87 text-align: center;
88 margin-bottom: 2rem;
89}
90
91.toggle-btn {
92 padding: 0.75rem 2rem;
93 border: 2px solid #ff0055;
94 background: rgba(255, 0, 85, 0.2);
95 color: #ff0055;
96 border-radius: 8px;
97 font-weight: bold;
98 cursor: pointer;
99 font-family: 'Courier New', monospace;
100 font-size: 1rem;
101 transition: all 0.3s ease;
102}
103
104.toggle-btn:hover {
105 background: rgba(255, 0, 85, 0.4);
106 transform: scale(1.05);
107}
108
109.hint {
110 color: #00b4d8;
111 font-size: 0.85rem;
112 margin-top: 0.5rem;
113}
114
115.unmounted-msg {
116 background: rgba(255, 0, 85, 0.1);
117 border: 2px solid #ff0055;
118 border-radius: 10px;
119 padding: 2rem;
120 text-align: center;
121}
122
123.unmounted-msg p {
124 color: #ff0055;
125 margin: 0.5rem 0;
126}
127</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. Co to są lifecycle hooks w Vue?
2. Kiedy wywoływany jest onMounted?
To 2 z 3 pytań do tej lekcji. Pozostałe rozwiążesz w grze.
Zadania praktyczne w grze
- Edytor kodu
Użyj onMounted do załadowania danych po zamontowaniu komponentu.
- Układanie w pionie
Ułóż lifecycle hooks w kolejności ich wywołania:
- Układanie w pionie
Ułóż fazy życia komponentu Vue w kolejności: