Kurs Vue.js · Moduł 6: Komponenty

Projekt: Mars Dashboard

2 min czytania
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 .vue z 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 scoped w 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.

Przydatne artykuły