Kurs Vue.js · Moduł 8: Sloty i komponenty dynamiczne
Slots - Podstawy
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:
- Komponent dziecko definiuje
<slot>w swoim template - to port dokujący. - Komponent rodzic przekazuje treść między tagami dziecka.
- 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. - 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. Czym jest slot w Vue.js?
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: