Kurs Vue.js · Moduł 8: Sloty i komponenty dynamiczne

Slots - Podstawy

4 min czytania
W tej lekcji6

Witaj w Magazynie Modułów NOVA LAB! Tu każdy kontener cargo ma standaryzowane porty dokujące - możesz wpinać różną zawartość, zachowując tę samą zewnętrzną strukturę. Zanim poznasz mechanizm, poczuj problem: budujesz komponent CargoBay z ramą, nagłówkiem i obramowaniem. W jednej zatoce leżą butle z tlenem, w drugiej lista filtrów, w trzeciej cały panel statusu. Props świetnie przenoszą tytuł albo liczbę, ale nie przeniosą kawałka szablonu z formatowaniem i innymi komponentami. Bez nowego narzędzia musiałbyś kopiować CargoBay dla każdego ładunku. W Vue tym narzędziem są slots - pozwalają rodzicowi wstrzykiwać dowolną treść do komponentu dziecka.

Czego się nauczysz

W Magazynie Modułów opanujesz cały system dokowania komponentów:

  • wstawiać treść do komponentu przez domyślny slot i dawać mu treść zapasową,
  • dzielić komponent na kilka portów dzięki named slots,
  • przekazywać dane z dziecka do treści slotu przez scoped slots,
  • przełączać panele stacji przez <component :is> i zachowywać ich stan w <KeepAlive>,
  • wyświetlać alarmy poza hierarchią komponentów przez <Teleport>,
  • ładować ciężkie moduły na żądanie przez async components i <Suspense>.

Czym jest Slot?

Slot to miejsce w template komponentu dziecka, które rodzic może wypełnić własną treścią. To jak port dokujący w cargo bay - komponent definiuje, gdzie jest otwór, a rodzic decyduje, co w niego włożyć. Dokumentacja Vue nazywa znacznik <slot> wylotem slotu (slot outlet), a to, co przekazuje rodzic, treścią slotu. Poniżej najpierw dziecko, czyli CargoBay.vue, a pod nim rodzic, App.vue, który wkłada między tagi komponentu dwa akapity:

1<!-- CargoBay.vue (dziecko) -->
2<template>
3  <div class="cargo-bay">
4    <h2>Cargo Bay Alpha</h2>
5    <div class="content">
6      <slot></slot>
7    </div>
8  </div>
9</template>
10
11<!-- App.vue (rodzic) -->
12<template>
13  <CargoBay>
14    <p>Oxygen Tank OX-2087</p>
15    <p>Weight: 250 kg</p>
16  </CargoBay>
17</template>

Rodzic umieszcza treść między tagami komponentu, a ta treść trafia w miejsce <slot></slot>. Rama, nagłówek "Cargo Bay Alpha" i klasy CSS nie zmieniły się ani o piksel - CargoBay nie wie, co przewozi, i nie musi tego wiedzieć.

Jak Vue renderuje slot

Warto rozumieć, co dzieje się pod maską, bo to wyjaśnia zachowanie slotów w kolejnych lekcjach. Cały proces przebiega w czterech krokach:

  1. Komponent dziecko definiuje <slot> w swoim template - to port dokujący.
  2. Komponent rodzic przekazuje treść między tagami dziecka.
  3. Gdy Vue renderuje dziecko, zastępuje <slot> przekazaną treścią. Technicznie kompilator zamienia treść rodzica w funkcję, a dziecko wywołuje ją dokładnie w miejscu wylotu.
  4. Treść pojawia się w DOM w pozycji slotu, u nas wewnątrz div.content.

Sam znacznik <slot> nie zostaje w gotowym HTML - w przeglądarce zobaczysz tylko akapity rodzica wewnątrz ramy dziecka.

Fallback Content

Co, jeśli rodzic zostawi port pusty? Treść wpisana między <slot> a </slot> w dziecku jest treścią zapasową (fallback) i wyświetla się tylko wtedy, gdy rodzic nic nie przekaże:

1<!-- CargoBay.vue -->
2<template>
3  <div class="cargo-bay">
4    <slot>
5      <p>Cargo bay pusty - oczekuje na ekwipunek</p>
6    </slot>
7  </div>
8</template>
9
10<!-- Użycie bez treści - wyświetli fallback -->
11<CargoBay />
12
13<!-- Użycie z treścią - zastąpi fallback -->
14<CargoBay>
15  <p>Water Recycler Unit</p>
16</CargoBay>

Pierwsze użycie pokaże komunikat o pustym cargo bay, a drugie zastąpi go w całości opisem recyklera wody. Fallback nie łączy się z treścią rodzica: na ekranie pojawia się albo jedno, albo drugie.

Slot Scope

Treść slotu jest kompilowana w scope rodzica, nie dziecka. To znaczy, że wewnątrz treści slotu masz dostęp do danych rodzica, jak w przykładzie ze zmienną moduleName zadeklarowaną w App.vue:

1<!-- App.vue -->
2<script setup>
3import { ref } from 'vue'
4const moduleName = ref('Oxygen Generator')
5</script>
6
7<template>
8  <CargoBay>
9    <!-- moduleName pochodzi z RODZICA -->
10    <p>{{ moduleName }}</p>
11  </CargoBay>
12</template>

Vue szuka moduleName tam, gdzie treść została napisana, czyli w App.vue. Działa to też w drugą stronę: treść slotu nie widzi danych dziecka, więc zmienna zadeklarowana w CargoBay byłaby w niej niedostępna. Jak mimo to pokazać w slocie dane dziecka, dowiesz się w lekcji o scoped slots.

Wiele elementów w slocie

Do jednego portu możesz wstawić dowolnie dużo elementów naraz: nagłówek, listę, a nawet inny komponent, jak StatusBadge wyświetlający odznakę statusu ładunku:

1<template>
2  <CargoBay>
3    <h3>Critical Equipment</h3>
4    <ul>
5      <li>Oxygen Tank</li>
6      <li>Water Filter</li>
7    </ul>
8    <StatusBadge status="loaded" />
9  </CargoBay>
10</template>

Wszystko między tagami trafia do jednego, domyślnego slotu w tej samej kolejności, w jakiej to zapisałeś. Gdy zechcesz rozdzielić treść na kilka miejsc, na przykład nagłówek osobno i przyciski osobno, potrzebne będą named slots z następnej lekcji.

Moja rada: gdy komponent ma pokazywać cudzą treść - tekst z formatowaniem, przyciski, inne komponenty - sięgaj po slot, a nie po prop z napisem. Props zostaw dla prostych wartości, takich jak tytuł czy liczba. I zawsze dawaj slotowi sensowny fallback, bo pusty cargo bay bez komunikatu wygląda na awarię. W edytorze poniżej CargoBay pojawia się trzy razy: z opisem butli, z listą modułów i zupełnie pusty, żebyś zobaczył fallback w akcji.

Zapamiętaj: slot to port dokujący - komponent daje ramę, a rodzic decyduje, jaki ładunek w nią wpiąć.

Kod do tej lekcji: App.vue
1<script setup>
2import CargoBay from './CargoBay.vue'
3</script>
4
5<template>
6  <div class="nova-lab">
7    <h1>NOVA LAB - demo slotów</h1>
8
9    <!-- Slot z treścią -->
10    <CargoBay>
11      <h3>Butla z tlenem OX-2087</h3>
12      <p>Waga: 250 kg | Status: załadowana</p>
13    </CargoBay>
14
15    <!-- Slot z komponentem i HTML -->
16    <CargoBay>
17      <ul>
18        <li>Filtr wody WF-01</li>
19        <li>Ogniwo słoneczne SC-44</li>
20        <li>Osłona termiczna TS-12</li>
21      </ul>
22    </CargoBay>
23
24    <!-- Slot pusty - wyświetli fallback -->
25    <CargoBay />
26  </div>
27</template>
28
29<style scoped>
30.nova-lab {
31  background: #0a0e27;
32  color: #00ff88;
33  min-height: 100vh;
34  padding: 20px;
35  font-family: monospace;
36}
37h1 { color: #00ff88; margin-bottom: 20px; }
38</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. Czym jest slot w Vue.js?

  2. 2. Co się stanie, gdy rodzic nie przekaże treści do slotu, a slot ma fallback content?

To 2 z 3 pytań do tej lekcji. Pozostałe rozwiążesz w grze.

Zadania praktyczne w grze

  • Edytor kodu

    Stwórz komponent CargoBay z tagiem <slot> i fallback content. Użyj go w App.vue wstawiając własną treść.

  • Klikanie w kolejności

    Ułóż kod użycia komponentu CargoBay z treścią slotu:

  • Edytor kodu

    Stwórz komponent AlertBox ze slotem i fallback content 'Brak alertów'. Użyj go raz z treścią i raz bez.

  • Układanie w pionie

    Ułóż etapy renderowania slotu od definicji do wyświetlenia:

Przydatne artykuły