Kurs Vue.js · Moduł 7: Props i zdarzenia
Podstawy Props
W tej lekcji6
Witaj w Stacji Komunikacji NOVA LAB! Tu systemy orbitalne przekazują parametry misji do modułów marsjańskich - podobnie jak komponenty-rodzice przekazują props dzieciom. Każdy sygnał się liczy, bo zależnie od położenia planet wiadomość z Ziemi leci na Marsa od około 3 do ponad 20 minut.
Wyobraź sobie panel, który pokazuje stan jednego modułu stacji: nazwę i temperaturę. Generator tlenu, filtr wody i szklarnia wyglądają na nim identycznie, różnią się tylko danymi. Gdyby komponent miał te dane wpisane na sztywno, musiałbyś skopiować go trzy razy i przy każdej poprawce pilnować trzech plików. Potrzebujesz jednego komponentu, któremu parametry podajesz z zewnątrz - i właśnie do tego służą props.
Czego się nauczysz
W Stacji Komunikacji opanujesz wszystkie kanały łączności między komponentami:
- przekazywać dane z rodzica do dziecka przez props, a potem je typować i walidować,
- wysyłać sygnały zwrotne z dziecka do rodzica przez własne zdarzenia,
- budować komponenty obsługujące
v-model, - rozgłaszać dane w głąb drzewa komponentów przez provide i inject,
- łączyć te kanały w jedną spójną architekturę łączności.
Czym są Props?
Props (od angielskiego properties) to mechanizm przekazywania danych z komponentu rodzica do komponentu dziecka. To jednokierunkowy przepływ danych - jak sygnał radiowy z Ziemi na Marsa. Dziecko deklaruje listę parametrów makrem defineProps, które działa w <script setup> bez importu, a rodzic podaje wartości jak atrybuty w szablonie:
1<!-- Parent.vue -->
2<template>
3 <MarsModule name="Oxygen Generator" :temperature="-63" />
4</template>
5
6<!-- MarsModule.vue -->
7<script setup>
8const props = defineProps(['name', 'temperature'])
9</script>
10
11<template>
12 <div>{{ name }}: {{ temperature }}°C</div>
13</template>Zwróć uwagę na dwukropek przed temperature. To skrót v-bind, dzięki któremu -63 trafia do dziecka jako liczba, a nie tekst; atrybut name bez dwukropka przekazuje zwykły napis. W szablonie dziecka props są dostępne wprost po nazwie, a w skrypcie przez obiekt zwrócony z defineProps, czyli props.name. Rodzic nie zmienił w dziecku ani linijki - podał tylko wartości.
Typowane Props
Tablica nazw mówi, jakie sygnały moduł przyjmuje, ale nie mówi, jakiego są rodzaju. Składnia obiektowa pozwala przypisać każdej nazwie typ, czyli konstruktor taki jak String, Number, Boolean, Array albo Object - to jak specyfikacja protokołu transmisji:
1<script setup>
2const props = defineProps({
3 name: String,
4 temperature: Number,
5 critical: Boolean,
6 modules: Array,
7 config: Object
8})
9</script>Typ niczego nie konwertuje. Jeśli rodzic poda tekst tam, gdzie oczekujesz liczby, Vue w trybie deweloperskim wypisze w konsoli ostrzeżenie, ale wartość i tak dotrze do komponentu - typy to system wczesnego ostrzegania, a nie zapora. Nazwy złożone deklaruj w camelCase, na przykład moduleName, a w szablonie rodzica możesz zapisać je jako module-name, bo Vue dopasuje obie formy.
Przekazywanie obiektu przez v-bind
Czasem dane modułu masz już zebrane w jednym obiekcie. Zamiast przepisywać każde pole osobno, przekaż cały obiekt przez v-bind bez argumentu - każda jego właściwość trafi do dziecka jako osobny prop:
1<template>
2 <!-- Pojedynczo -->
3 <MarsModule :name="data.name" :status="data.status" />
4
5 <!-- Wszystkie naraz -->
6 <MarsModule v-bind="data" />
7</template>
8
9<script setup>
10import { reactive } from 'vue'
11
12const data = reactive({
13 name: 'Oxygen Generator',
14 status: 'operational',
15 temperature: -63
16})
17</script>Druga linia przekazuje także temperature, której pierwsza w ogóle nie wymienia. Dzięki reactive każda zmiana w obiekcie data od razu dociera do dziecka. Pola, których dziecko nie zadeklarowało jako props, nie przepadają - staną się atrybutami przekazywanymi dalej, którym przyjrzymy się w lekcji o fallthrough attributes.
Boolean Props
Props typu Boolean naśladują atrybuty HTML w rodzaju disabled, dlatego mają własną regułę: obecność atrybutu oznacza true, a jego brak oznacza false:
1<template>
2 <SystemModule critical /> <!-- critical = true -->
3 <SystemModule :critical="true" /> <!-- jawne true -->
4 <SystemModule :critical="false" /> <!-- jawne false -->
5 <SystemModule /> <!-- critical = false (default) -->
6</template>Ta reguła dotyczy wyłącznie propsów zadeklarowanych jako Boolean. Nieobecny prop każdego innego typu ma wartość undefined, a nie false, więc szablon dziecka powinien być na to przygotowany.
Jednokierunkowy przepływ danych
Najważniejsza zasada tej lokacji: dane płyną tylko w dół. Rodzic jest właścicielem danych, a dziecko dostaje je do odczytu. Gdy rodzic zmieni wartość, Vue sam odświeży dziecko, ale dziecko nie może nadpisać propsa:
1<script setup>
2const props = defineProps(['title'])
3
4// ŹLE - nie modyfikuj props!
5// props.title = 'Nowy tytuł'
6
7// Stwórz lokalną kopię
8import { ref } from 'vue'
9const localTitle = ref(props.title)
10</script>Próba przypisania kończy się ostrzeżeniem w konsoli, że cel jest tylko do odczytu, a wartość zostaje bez zmian. Lokalna kopia w ref sprawdza się, gdy prop jest tylko wartością startową. Pamiętaj jednak, czego kopia nie robi: gdy rodzic później zmieni title, zmienna localTitle już za nim nie pójdzie. Wartość przeliczaną z propsa opisuj przez computed, a gdy dziecko naprawdę chce zmienić dane rodzica, niech wyśle mu sygnał - to temat lekcji o custom events.
Moja rada: od pierwszego komponentu deklaruj props w składni obiektowej z typami. Tablica nazw wystarcza w szkicu, ale typy wyłapią pomyłkę rodzica, zanim zauważy ją załoga na Marsie. W następnej lekcji dołożysz do nich walidację: wymagane pola, wartości domyślne i własne warunki. W edytorze poniżej ten sam moduł pojawia się trzy razy - z propsami podanymi pojedynczo, przez v-bind i z flagą critical.
Zapamiętaj: props to sygnał z centrali - moduł go odbiera i wyświetla, ale nigdy go nie nadpisuje.
Kod do tej lekcji: App.vue
1<script setup>
2import { reactive } from 'vue'
3import MarsModule from './MarsModule.vue'
4
5const moduleData = reactive({
6 name: 'Generator tlenu Alfa',
7 status: 'sprawny',
8 temperature: -63
9})
10</script>
11
12<template>
13 <div class="nova-lab">
14 <h1>NOVA LAB - demo props</h1>
15
16 <!-- Przekazanie props pojedynczo -->
17 <MarsModule
18 :name="moduleData.name"
19 :status="moduleData.status"
20 :temperature="moduleData.temperature"
21 />
22
23 <!-- Przekazanie wszystkich props naraz -->
24 <MarsModule v-bind="moduleData" />
25
26 <!-- Boolean prop - obecność = true -->
27 <MarsModule v-bind="moduleData" critical />
28 </div>
29</template>
30
31<style scoped>
32.nova-lab {
33 background: #0a0e27;
34 color: #00ff88;
35 min-height: 100vh;
36 padding: 20px;
37 font-family: monospace;
38}
39h1 { color: #00ff88; margin-bottom: 20px; }
40</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 są props w Vue.js?
2. Jak zadeklarować props w <script setup>?
To 2 z 3 pytań do tej lekcji. Pozostałe rozwiążesz w grze.
Zadania praktyczne w grze
- Edytor kodu
Stwórz komponent MarsSignal przyjmujący props message i delay.
- Klikanie w kolejności
Ułóż definicję props z typem:
- Edytor kodu
Stwórz komponent SystemModule z typowanymi props: name (String), temperature (Number), critical (Boolean).
- Układanie w pionie
Ułóż przepływ danych Props od komponentu nadrzędnego do wyświetlenia: