Kurs Vue.js · Moduł 6: Komponenty
Projekt: Mars Dashboard
W tej lekcji2
Finalny projekt Drukarni 3D! Zbuduj kompletny dashboard stacji NOVA LAB z wieloma komponentami, cyklami życia i composables.
Podsumowanie modułu
W tym module poznałeś fundamenty budowania aplikacji Vue.js z komponentów - tak jak stacja NOVA LAB składa się z modułowych części habitatu, Twoja aplikacja składa się z komponentów:
- Komponenty - tworzenie samodzielnych, wielokrotnego użytku elementów UI z własnym szablonem (
<template>), logiką (<script setup>) i stylami (<style>). Każdy komponent to osobny plik.vuez jasno określoną odpowiedzialnością - Import komponentów - lokalne rejestrowanie (import w danym komponencie) oraz globalne rejestrowanie (dostępne w całej aplikacji przez
app.component()). W Composition API z<script setup>zaimportowane komponenty są automatycznie dostępne w szablonie - Kompozycja - budowanie złożonych interfejsów z prostych, mniejszych komponentów. Wzorzec parent-child, przekazywanie danych w dół i emitowanie zdarzeń w górę drzewa komponentów
- Lifecycle hooks -
onMounted(po zamontowaniu w DOM),onUpdated(po aktualizacji),onUnmounted(po odmontowaniu),onBeforeMount,onBeforeUpdate- pozwalają wykonywać kod w kluczowych momentach życia komponentu - Scoped styles - izolacja stylów w komponentach dzięki atrybutowi
scopedw tagu<style>. Style są automatycznie ograniczone do danego komponentu i nie wpływają na resztę aplikacji - Composables - funkcje wielokrotnego użytku (np.
useTimer,useFetch), które enkapsulują reaktywny stan i logikę. Pozwalają współdzielić logikę między komponentami bez duplikowania kodu
Wymagania projektu
Zbuduj Mars Dashboard - centralny panel kontrolny stacji NOVA LAB, który składa się z wielu współpracujących komponentów:
1. Komponent StatusBar
Wyświetla ogólny status stacji: nazwę stacji, aktualną datę i godzinę (aktualizowana co sekundę przez composable), poziom energii (pasek progresu), temperaturę zewnętrzną i ciśnienie w habitacie. Użyj onMounted do inicjalizacji danych i onUnmounted do czyszczenia timerów.
2. Komponent ModuleList
Lista modułów habitatowych (np. Oxygen Generator, Water Recycler, Solar Panels, Communication Array) z możliwością przełączania statusu każdego modułu (ACTIVE/STANDBY/OFFLINE). Każdy moduł powinien być renderowany jako osobny komponent ModuleCard z odpowiednimi kolorami statusu.
3. Composable useTimer
Stwórz composable useTimer, który odlicza czas od startu aplikacji (mission uptime). Użyj ref dla stanu, onMounted do uruchomienia setInterval, i onUnmounted do wyczyszczenia interwału (clearInterval). Composable powinien zwracać sformatowany czas (HH:MM:SS).
4. Scoped styles
Każdy komponent powinien mieć własne izolowane style z atrybutem scoped. Użyj ciemnego motywu kosmicznego (ciemne tła, neonowe kolory akcentowe: zielony dla ACTIVE, żółty dla STANDBY, czerwony dla OFFLINE). Zadbaj o responsywność - dashboard powinien działać na różnych rozmiarach ekranu.
Rozbuduj starter poniżej!
Kod do tej lekcji: App.vue
1<script setup>
2import { ref, onMounted, onUnmounted } from 'vue'
3
4// Mars Dashboard - Projekt koncowy modulu 6
5// Demonstracja komponentow, lifecycle hooks, composables
6
7// Composable: useTimer
8function useTimer() {
9 const elapsed = ref(0)
10 let interval = null
11
12 onMounted(() => {
13 interval = setInterval(() => { elapsed.value++ }, 1000)
14 })
15
16 onUnmounted(() => {
17 if (interval) clearInterval(interval)
18 })
19
20 const formatted = ref('')
21 setInterval(() => {
22 const h = Math.floor(elapsed.value / 3600)
23 const m = Math.floor((elapsed.value % 3600) / 60)
24 const s = elapsed.value % 60
25 formatted.value = [h, m, s].map(v => String(v).padStart(2, '0')).join(':')
26 }, 1000)
27
28 return { elapsed, formatted }
29}
30
31const { formatted: uptime } = useTimer()
32
33const stationName = ref('NOVA LAB Mars Base')
34const energyLevel = ref(87)
35
36const modules = ref([
37 { id: 1, name: 'Laboratorium', status: true },
38 { id: 2, name: 'Reaktor', status: true },
39 { id: 3, name: 'Komunikacja', status: false },
40 { id: 4, name: 'Szklarnia', status: true },
41 { id: 5, name: 'Warsztat', status: true }
42])
43
44function toggleModule(id) {
45 const mod = modules.value.find(m => m.id === id)
46 if (mod) mod.status = !mod.status
47}
48
49// TODO: Wydziel StatusBar i ModuleList do osobnych komponentow
50// TODO: Dodaj scoped styles do kazdego komponentu
51// TODO: Rozbuduj composable useTimer o start/stop
52</script>
53
54<template>
55 <div class="mars-dashboard">
56 <!-- StatusBar -->
57 <header class="status-bar">
58 <h1>{{ stationName }}</h1>
59 <div class="info">
60 <span>Uptime: {{ uptime }}</span>
61 <span>Energia: {{ energyLevel }}%</span>
62 </div>
63 </header>
64
65 <!-- ModuleList -->
66 <section class="module-list">
67 <h2>Moduly Habitatu</h2>
68 <div
69 v-for="mod in modules"
70 :key="mod.id"
71 class="module-item"
72 :class="{ online: mod.status, offline: !mod.status }"
73 @click="toggleModule(mod.id)"
74 >
75 <span class="dot"></span>
76 <span>{{ mod.name }}</span>
77 <span class="tag">{{ mod.status ? 'ONLINE' : 'OFFLINE' }}</span>
78 </div>
79 </section>
80 </div>
81</template>
82
83<style scoped>
84.mars-dashboard { background: #0a0a2e; color: #e0e0ff; padding: 2rem; min-height: 100vh; font-family: monospace; }
85.status-bar { border-bottom: 1px solid #333; padding-bottom: 1rem; margin-bottom: 1rem; }
86.status-bar h1 { color: #ff6b35; margin: 0; }
87.info { display: flex; gap: 2rem; color: #888; margin-top: 0.5rem; }
88.module-list h2 { color: #00d4ff; }
89.module-item { display: flex; align-items: center; gap: 1rem; padding: 0.8rem; margin: 0.5rem 0; border-radius: 4px; cursor: pointer; background: #1a1a4e; }
90.module-item.online { border-left: 3px solid #00ff88; }
91.module-item.offline { border-left: 3px solid #ff4444; opacity: 0.7; }
92.dot { width: 8px; height: 8px; border-radius: 50%; }
93.online .dot { background: #00ff88; }
94.offline .dot { background: #ff4444; }
95.tag { margin-left: auto; font-size: 0.8rem; }
96</style>Widzisz błąd w tej lekcji?
Zadania praktyczne w grze
- Edytor kodu
Zbuduj MarsDashboard z wieloma komponentami, composables, lifecycle hooks i scoped styles.