Kurs Vue.js · Moduł 6: Komponenty
Kompozycja Komponentów
W tej lekcji6
Dr. Nova kontynuuje: "Stacja NOVA LAB nie jest jednym monolitycznym blokiem - to drzewo modułów: stacja główna zawiera sekcje, sekcje zawierają pomieszczenia, a pomieszczenia zawierają urządzenia. Tak samo budujemy aplikacje Vue - z hierarchii komponentów."
Wyobraź sobie panel dowodzenia zapisany w jednym pliku na tysiąc linii. Nagłówek, lista modułów, przyciski sterujące i stopka mieszają się ze sobą, a błąd w jednym przycisku trzeba wyszukiwać w całym pliku. Taki monolit trudno czytać, testować i rozwijać.
Czym Jest Kompozycja Komponentów?
Kompozycja to wzorzec budowania złożonych interfejsów z prostszych, mniejszych komponentów. Zamiast jednego ogromnego komponentu z tysiącem linii kodu, dzielimy UI na logiczne części, a komponent główny tylko je układa:
1<!-- App.vue - korzeń drzewa komponentów -->
2<template>
3 <div class="station">
4 <StationHeader />
5 <main>
6 <ModuleList />
7 <ControlPanel />
8 </main>
9 <StationFooter />
10 </div>
11</template>
12
13<script setup>
14import StationHeader from './components/StationHeader.vue'
15import ModuleList from './components/ModuleList.vue'
16import ControlPanel from './components/ControlPanel.vue'
17import StationFooter from './components/StationFooter.vue'
18</script>Szablon App.vue czyta się teraz jak spis sekcji stacji. Nie zmienił się przy tym sposób użycia komponentów - to te same importy i znaczniki co w poprzedniej lekcji, tylko jest ich więcej.
Relacja Rodzic-Dziecko (Parent-Child)
Kiedy komponent używa innego komponentu w swoim szablonie, powstaje relacja rodzic-dziecko. Lista modułów wyświetla trzy karty:
1<!-- ModuleList.vue (RODZIC) -->
2<template>
3 <div class="module-list">
4 <h2>Moduły stacji</h2>
5 <!-- ModuleCard to DZIECKO ModuleList -->
6 <ModuleCard />
7 <ModuleCard />
8 <ModuleCard />
9 </div>
10</template>
11
12<script setup>
13import ModuleCard from './ModuleCard.vue'
14</script>W tym przykładzie:
ModuleListjest rodzicem (parent)- Każdy
ModuleCardjest dzieckiem (child) Appjest rodzicemModuleList- tworzy się hierarchia
Role są względne: ModuleList jest dzieckiem dla App i jednocześnie rodzicem dla kart. Gdyby każda karta wyświetlała jeszcze komponent ModulePanel, byłby on wnukiem ModuleList (grandchild), a App stałby się dla kart dziadkiem.
Drzewo Komponentów
Każda aplikacja Vue tworzy drzewo komponentów - hierarchiczną strukturę zaczynającą się od komponentu głównego (App.vue), którą zobaczysz też w zakładce Components w DevTools:
1App
2├── StationHeader
3│ ├── Logo
4│ └── NavigationMenu
5│ ├── NavItem (Dashboard)
6│ ├── NavItem (Modules)
7│ └── NavItem (Settings)
8├── ModuleList
9│ ├── ModuleCard (Oxygen Generator)
10│ ├── ModuleCard (Plasma Core)
11│ └── ModuleCard (Water Recycler)
12├── ControlPanel
13│ ├── StatusDisplay
14│ └── ActionButtons
15└── StationFooterIm głębiej w drzewie, tym bardziej wyspecjalizowany jest komponent. App to ogólna struktura, a NavItem to konkretny element menu.
Zagnieżdżanie Komponentów
Komponenty mogą zawierać inne komponenty na dowolną głębokość. Panel sterowania składa się z wyświetlacza statusu i trzech przycisków:
1<!-- ControlPanel.vue -->
2<template>
3 <section class="control-panel">
4 <StatusDisplay />
5 <div class="actions">
6 <ActionButton label="Uruchom" icon="play" />
7 <ActionButton label="Zatrzymaj" icon="stop" />
8 <ActionButton label="Restart" icon="refresh" />
9 </div>
10 </section>
11</template>
12
13<script setup>
14import StatusDisplay from './StatusDisplay.vue'
15import ActionButton from './ActionButton.vue'
16</script>Wyświetlacz statusu to kolejny poziom zagnieżdżenia, bo sam składa się z trzech osobnych wskaźników stanu stacji:
1<!-- StatusDisplay.vue - kolejny poziom zagnieżdżenia -->
2<template>
3 <div class="status">
4 <StatusIndicator label="Energia" value="87%" color="green" />
5 <StatusIndicator label="Tlen" value="95%" color="green" />
6 <StatusIndicator label="Temperatura" value="22°C" color="yellow" />
7 </div>
8</template>
9
10<script setup>
11import StatusIndicator from './StatusIndicator.vue'
12</script>Atrybuty label, value i color to dane, które rodzic przekazuje dziecku. Żeby dziecko mogło je odebrać, deklaruje je makrem defineProps. Tego makra nie importujesz - kompilator <script setup> zna je sam. Tak mógłby wyglądać przycisk z panelu sterowania:
1<!-- ActionButton.vue -->
2<template>
3 <button class="action">{{ label }}</button>
4</template>
5
6<script setup>
7defineProps(['label', 'icon'])
8</script>Każdy z trzech przycisków pokazuje teraz własny napis, choć plik komponentu jest jeden. Pełne możliwości props, z typami i walidacją, poznasz w Stacji Komunikacji.
Trzy Kroki do Użycia Dziecka
Niezależnie od głębokości drzewa, użycie komponentu dziecka zawsze wymaga tych samych trzech kroków: tworzysz plik .vue, importujesz go w rodzicu i używasz jego znacznika w szablonie rodzica. Jeśli pominiesz import, Vue nie rozpozna znacznika.
Przepływ Danych w Drzewie
W drzewie komponentów Vue dane przepływają w określonych kierunkach:
- W dół (rodzic → dziecko): przez props - rodzic przekazuje dane dzieciom
- W górę (dziecko → rodzic): przez events - dziecko informuje rodzica o zdarzeniach
Ten wzorzec nazywamy jednokierunkowym przepływem danych (one-way data flow). Szczegóły props i events poznasz w następnym module - na razie zapamiętaj kierunki:
1 App (dane)
2 │
3 ▼ props (w dół)
4 ModuleList
5 │
6 ▼ props (w dół)
7 ModuleCard
8 │
9 ▲ events (w górę)
10 ModuleList
11 │
12 ▲ events (w górę)
13 App (reaguje)Dziecko nie zmienia danych rodzica bezpośrednio - props są dla niego tylko do odczytu - lecz melduje zdarzenie, a decyzję podejmuje rodzic. Dzięki temu zawsze wiesz, gdzie szukać źródła zmiany - w komponencie, który dane posiada. W edytorze poniżej zobaczysz drzewo App, MarsBase, MarsModule i ModuleDetail, zbudowane właśnie w ten sposób.
Zapamiętaj: stacja to drzewo modułów - każdy komponent robi jedną rzecz, rodzic składa dzieci w całość, a dane płyną w dół, zdarzenia w górę.
Kod do tej lekcji: App.vue
1<script setup>
2import MarsBase from './MarsBase.vue'
3</script>
4
5<template>
6 <div class="composition-lab">
7 <div class="header">
8 <h1>Kompozycja Komponentow - NOVA LAB</h1>
9 <p class="subtitle">Budowanie zlozonego UI z komponentow</p>
10 </div>
11
12 <div class="info-panel">
13 <h3>Kompozycja komponentow</h3>
14 <p>Komponenty mozna <strong>zagniezdac</strong> w sobie tworzac hierarchie:</p>
15 <p>App → MarsBase → MarsModule → ModuleDetail</p>
16 <p>Kazdy poziom odpowiada za swoja czesc interfejsu</p>
17 </div>
18
19 <MarsBase />
20 </div>
21</template>
22
23<style scoped>
24.composition-lab {
25 background: linear-gradient(135deg, #0a0e27 0%, #1a1e3f 100%);
26 min-height: 100vh;
27 padding: 2rem;
28 color: #ffffff;
29 font-family: 'Courier New', monospace;
30}
31
32.header {
33 text-align: center;
34 margin-bottom: 2rem;
35 padding-bottom: 1rem;
36 border-bottom: 2px solid #00ff88;
37}
38
39.header h1 {
40 font-size: 2rem;
41 color: #00ff88;
42 text-shadow: 0 0 20px rgba(0, 255, 136, 0.5);
43 margin: 0;
44}
45
46.subtitle {
47 color: #00b4d8;
48 font-size: 1.1rem;
49 margin-top: 0.5rem;
50}
51
52.info-panel {
53 background: rgba(0, 180, 216, 0.1);
54 border: 2px solid #00b4d8;
55 border-radius: 10px;
56 padding: 1.5rem;
57 margin-bottom: 2rem;
58}
59
60.info-panel h3 {
61 color: #00b4d8;
62 margin-top: 0;
63}
64
65.info-panel strong {
66 color: #00ff88;
67}
68</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 jest kompozycja komponentów?
2. Jaka jest relacja między komponentem używającym a używanym?
To 2 z 3 pytań do tej lekcji. Pozostałe rozwiążesz w grze.
Zadania praktyczne w grze
- Edytor kodu
Stwórz MarsBase składający się z wielu komponentów MarsModule.
- Układanie w pionie
Ułóż hierarchię komponentów od korzenia do najgłębszego:
- Klikanie w kolejności
Ułóż kroki użycia komponentu dziecka: