Kurs Vue.js · Moduł 2: Składnia szablonów
Wiązanie Klas i Stylów
W tej lekcji4
Jak systemy NOVA LAB dostosowują hologramy do sytuacji, tak Vue pozwala dynamicznie zarządzać stylami interfejsu.
Panel statusu ma świecić na zielono, gdy system działa, i pulsować na czerwono przy awarii. Z samym v-bind musiałbyś sklejać nazwy klas w stringu, na przykład 'panel ' + (isOnline ? 'active' : ''), a przy trzech warunkach taki zapis staje się nieczytelny. Dlatego :class i :style mają w Vue specjalne uprawnienia: przyjmują nie tylko tekst, ale też obiekty i tablice.
Wiązanie Klas - Składnia Obiektowa
W składni obiektowej klucz to nazwa klasy, a wartość to warunek: gdy jest prawdziwy, klasa trafia do elementu. Nazwy z myślnikiem, jak 'status-critical', piszesz w cudzysłowie. :class może stać obok zwykłego atrybutu class - klasy stałe zostają, a dynamiczne są dopisywane.
1<template>
2 <div class="status-panel" style="background: #0a0e27;">
3 <div :class="{ active: isOnline, 'status-critical': hasCriticalError }">
4 System Status Display
5 </div>
6
7 <!-- Z computed -->
8 <div :class="statusClasses">Computed Status</div>
9 </div>
10</template>Przy isOnline równym true i braku awarii pierwszy wewnętrzny div dostanie tylko klasę active, a zewnętrzny zachowa swoje status-panel. Drugi div bierze gotowy obiekt klas ze zmiennej statusClasses, która jest właściwością computed.
Computed to wartość wyliczana: podajesz funkcję, a Vue zapamiętuje jej wynik i liczy go ponownie tylko wtedy, gdy zmienią się refy użyte w środku. Czytasz ją jak ref, a szczegóły poznasz w module 4. Tutaj zwraca obiekt klas:
1<script setup>
2import { ref, computed } from 'vue'
3
4const isOnline = ref(true)
5const hasCriticalError = ref(false)
6
7const statusClasses = computed(() => ({
8 active: isOnline.value,
9 'status-critical': hasCriticalError.value,
10 'neon-glow': isOnline.value
11}))
12</script>Gdy ustawisz hasCriticalError.value = true, obiekt przeliczy się sam i panel dostanie klasę status-critical. Dokumentacja Vue nazywa ten wzorzec częstym i bardzo użytecznym. Mój wybór: obiekt wprost w szablonie dla jednej lub dwóch klas, computed przy większej liczbie warunków. Same klasy definiuje zwykły CSS:
1<style>
2.active {
3 color: #00ff88;
4 border-left: 3px solid #00ff88;
5}
6.status-critical {
7 color: #ff4444;
8 animation: pulse 1s infinite;
9}
10.neon-glow {
11 box-shadow: 0 0 10px #00ff88;
12}
13@keyframes pulse {
14 0%, 100% { opacity: 1; }
15 50% { opacity: 0.5; }
16}
17</style>Vue decyduje wyłącznie o tym, które nazwy klas trafią do atrybutu class. Wygląd, animacja pulse i poświata pozostają sprawą CSS, dokładnie jak w pierwszym świecie.
Wiązanie Klas - Składnia Tablicowa
Tablica przydaje się, gdy nazwy klas same siedzą w zmiennych. Jej elementy to wyrażenia, więc możesz w nich użyć operatora warunkowego albo wstawić obiekt z poprzedniej sekcji:
1<template>
2 <div class="reactor-display">
3 <div :class="[activeClass, statusClass]">
4 Array syntax
5 </div>
6
7 <!-- Z warunkiem -->
8 <div :class="[isOnline ? 'online' : 'offline', statusClass]">
9 Warunkowa klasa
10 </div>
11
12 <!-- Mieszane -->
13 <div :class="[{ active: isOnline }, statusClass]">
14 Mixed
15 </div>
16 </div>
17</template>
18
19<script setup>
20import { ref } from 'vue'
21
22const activeClass = ref('reactor-active')
23const statusClass = ref('status-nominal')
24const isOnline = ref(true)
25</script>Pierwszy div dostanie klasy reactor-active status-nominal, drugi online status-nominal, a trzeci active status-nominal. W tablicy stoją wartości zmiennych, nie ich nazwy: activeClass oznacza napis reactor-active. Gdy w tablicy pojawia się ternary z pustym stringiem, wybieraj wersję mieszaną z obiektem - jest krótsza i czytelniejsza.
Wiązanie Stylów Inline
:style przyjmuje obiekt właściwości CSS. Zalecane są klucze w camelCase, jak fontSize, choć klucze z myślnikiem w cudzysłowie też działają. Liczby potrzebują jednostki, dlatego dopisujemy 'px'. Możesz też podać gotowy obiekt stylów albo tablicę obiektów:
1<template>
2 <div class="hologram-container">
3 <!-- Składnia obiektowa -->
4 <div :style="{ color: neonColor, fontSize: fontSize + 'px' }">
5 Inline styles
6 </div>
7
8 <!-- Z obiektem -->
9 <div :style="panelStyle">Style object</div>
10
11 <!-- Składnia tablicowa - wiele obiektów -->
12 <div :style="[baseStyles, highlightStyles]">
13 Multiple style objects
14 </div>
15 </div>
16</template>Pierwszy div dostanie color i font-size: 18px. W tablicy obiekty łączą się w jeden, a przy powtórzonej właściwości wygrywa późniejszy. Dane pochodzą ze skryptu:
1<script setup>
2import { ref, reactive } from 'vue'
3
4const neonColor = ref('#00ff88')
5const fontSize = ref(18)
6
7const panelStyle = reactive({
8 color: '#00b4d8',
9 backgroundColor: '#0a0e27',
10 padding: '1rem',
11 borderRadius: '8px',
12 border: '1px solid #00ff88'
13})
14
15const baseStyles = { color: '#00b4d8' }
16const highlightStyles = { fontSize: '20px', textShadow: '0 0 10px #00b4d8' }
17</script>Zmiana fontSize.value = 24 powiększy tylko tekst pierwszego div. baseStyles i highlightStyles to zwykłe obiekty, nie reaktywne, więc ich późniejsza zmiana nie odświeży widoku - jako stałe sprawdzają się jednak bez zarzutu.
Gdy styl zależy od kilku warunków, przygotuj obiekt w computed, tak jak przy klasach:
1<script setup>
2import { ref, computed } from 'vue'
3
4const oxygenLevel = ref(18)
5
6const alertStyle = computed(() => ({
7 color: oxygenLevel.value < 20 ? '#ff4444' : '#00ff88',
8 fontWeight: oxygenLevel.value < 20 ? 'bold' : 'normal'
9}))
10</script>
11
12<template>
13 <p :style="alertStyle">Tlen: {{ oxygenLevel }}%</p>
14</template>W szablonie zostało jedno krótkie wiązanie, a cała logika mieszka w skrypcie. Mimo to polecam klasy zamiast stylów inline wszędzie, gdzie wygląd jest znany z góry. :style zostaw dla wartości naprawdę liczonych z danych, jak szerokość paska mocy.
Auto-prefixing
Vue potrafi dodawać prefiksy producentów przeglądarek, ale robi to oszczędnie: w trakcie działania sprawdza, czy bieżąca przeglądarka zna daną właściwość, i tylko gdy jej nie zna, próbuje wariantów z prefiksem:
1<div :style="{ transform: 'rotateX(45deg)' }">
2 <!-- Vue doda prefiks (np. -webkit-) tylko wtedy, gdy przeglądarka nie zna wersji bez prefiksu -->
3</div>Współczesne przeglądarki znają transform, więc zobaczysz go bez prefiksu. Możesz też podać tablicę wartości, na przykład ['-webkit-box', 'flex'] dla display - Vue użyje ostatniej obsługiwanej. W następnej lekcji zdecydujesz, czy element w ogóle ma istnieć, dzięki v-if i v-show.
Zapamiętaj: obiekt w :class to tablica przełączników - klucz to nazwa klasy, a wartość decyduje, czy lampka świeci.
Kod do tej lekcji: App.vue
1<script setup>
2import { ref, computed } from 'vue'
3
4const isOnline = ref(true)
5const hasCriticalError = ref(false)
6const modeClass = ref('mode-nominal')
7const neonColor = ref('#00ff88')
8const fontSize = ref(18)
9const oxygenLevel = ref(18)
10
11// obiekt stylów liczony z danych: czerwony i pogrubiony tekst, gdy tlenu jest za mało
12const alertStyle = computed(() => ({
13 color: oxygenLevel.value < 20 ? '#ff4444' : '#00ff88',
14 fontWeight: oxygenLevel.value < 20 ? 'bold' : 'normal'
15}))
16</script>
17
18<template>
19 <div class="hologram-lab">
20 <h1>Pracownia Hologramów: klasy i style</h1>
21
22 <div class="controls">
23 <button @click="isOnline = !isOnline">Przełącz zasilanie</button>
24 <button @click="hasCriticalError = !hasCriticalError">Symuluj awarię</button>
25 <button @click="fontSize += 2">Większy tekst</button>
26 <button @click="oxygenLevel += 5">Dodaj tlen</button>
27 </div>
28
29 <!-- Składnia obiektowa: klucz to nazwa klasy, wartość to warunek -->
30 <div class="panel" :class="{ active: isOnline, 'status-critical': hasCriticalError }">
31 Status systemu: {{ isOnline ? 'ONLINE' : 'OFFLINE' }}
32 </div>
33
34 <!-- Składnia tablicowa: nazwy klas ze zmiennej i z operatora warunkowego -->
35 <div class="panel" :class="[modeClass, isOnline ? 'online' : 'offline']">
36 Tryb pracy z tablicy klas
37 </div>
38
39 <!-- Składnia mieszana: obiekt wewnątrz tablicy -->
40 <div class="panel" :class="[{ active: isOnline }, modeClass]">
41 Tryb mieszany
42 </div>
43
44 <!-- :style z obiektem: klucze w camelCase, liczba z jednostką px -->
45 <p :style="{ color: neonColor, fontSize: fontSize + 'px' }">
46 Rozmiar tekstu: {{ fontSize }}px
47 </p>
48
49 <!-- :style z computed: logika zostaje w skrypcie -->
50 <p :style="alertStyle">Tlen: {{ oxygenLevel }}%</p>
51 </div>
52</template>
53
54<style scoped>
55.hologram-lab { background: #0a0e27; color: #e0e0ff; min-height: 100vh; padding: 1.5rem; font-family: 'Courier New', monospace; }
56h1 { color: #00b4d8; font-size: 1.4rem; }
57.controls { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 1rem; }
58button { background: transparent; border: 2px solid #00ff88; color: #00ff88; padding: 0.4rem 0.8rem; cursor: pointer; font-family: inherit; }
59.panel { border: 2px solid #334; border-radius: 8px; padding: 0.75rem; margin: 0.5rem 0; }
60.active { color: #00ff88; border-left: 4px solid #00ff88; }
61.status-critical { color: #ff4444; animation: pulse 1s infinite; }
62.online { box-shadow: 0 0 10px #00ff88; }
63.offline { opacity: 0.5; }
64.mode-nominal { border-color: #00b4d8; }
65@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
66</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. Jak warunkowo dodać klasę CSS w Vue?
2. Jak przypisać wiele klas dynamicznie przez array?
3. Jak dynamicznie ustawić kolor tekstu w Vue?
Zadania praktyczne w grze
- Edytor kodu
Użyj :class ze składnią obiektową i tablicową, aby warunkowo dodawać klasy.
- Klikanie w kolejności
Ułóż składnię dynamicznego stylu inline:
- Układanie w poziomie
Ułóż poprawną składnię bindowania klasy warunkowej:
- Układanie w pionie
Ułóż sposoby wiązania klas od prostego do mieszanego:
- Edytor kodu
Użyj :style do dynamicznego ustawienia kolorów i rozmiaru czcionki.