Kurs Vue.js · Moduł 6: Komponenty

Kompozycja Komponentów

4 min czytania
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:

  • ModuleList jest rodzicem (parent)
  • Każdy ModuleCard jest dzieckiem (child)
  • App jest rodzicem ModuleList - 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└── StationFooter

Im 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 &rarr; MarsBase &rarr; MarsModule &rarr; 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. 1. Co to jest kompozycja komponentów?

  2. 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:

Przydatne artykuły