Kurs Svelte i SvelteKit · Moduł 3: Reaktywność i runy
$state - Stan reaktywny
W tej lekcji5
$state to najpotężniejsza runa - tworzy reaktywny stan, który automatycznie aktualizuje UI przy zmianach.
Podstawowe użycie
<script>
// Liczba
let gold = $state(0);
// String
let vikingName = $state('Ragnar');
// Boolean
let isReady = $state(false);
// Array
let warriors = $state(['Bjorn', 'Ivar']);
// Object
let ship = $state({
name: 'Drakkar',
crew: 50,
cargo: []
});
</script>Aktualizacja stanu
<script>
let count = $state(0);
let items = $state(['a', 'b']);
let user = $state({ name: 'Viking', level: 1 });
function examples() {
// Liczby - jak zwykłe przypisanie
count += 1;
count = count * 2;
// Tablice - mutacje działają!
items.push('c'); // Działa w Svelte 5!
items[0] = 'x'; // Działa!
items = [...items, 'd']; // Też działa
// Obiekty - mutacje działają!
user.level += 1; // Działa!
user.name = 'Jarl'; // Działa!
}
</script>Głęboka reaktywność
$state tworzy głęboko reaktywny obiekt:
<script>
let kingdom = $state({
name: 'Nordheim',
villages: [
{ name: 'Svenheim', population: 500 },
{ name: 'Bjornvik', population: 300 }
]
});
function growVillage() {
// Zagnieżdżona mutacja - działa automatycznie!
kingdom.villages[0].population += 100;
}
</script>
<p>{kingdom.villages[0].name}: {kingdom.villages[0].population}</p>
<button onclick={growVillage}>Rozwijaj wioskę</button>$state z klasami
<script>
class Warrior {
name = $state('');
health = $state(100);
weapon = $state('Sword');
constructor(name) {
this.name = name;
}
takeDamage(amount) {
this.health -= amount;
}
}
let hero = new Warrior('Ragnar');
</script>
<p>{hero.name}: {hero.health} HP</p>
<button onclick={() => hero.takeDamage(10)}>
Zadaj obrażenia
</button>$state.raw - Bez głębokiej reaktywności
Czasem nie potrzebujesz głębokiej reaktywności:
<script>
// Tylko referencja jest reaktywna, nie zawartość
let battleLog = $state.raw([
{ year: 793, place: 'Lindisfarne' },
{ year: 845, place: 'Paryż' }
]);
// battleLog.push(...) NIE odświeży widoku - przypisz nową tablicę
function addBattle() {
battleLog = [...battleLog, { year: 866, place: 'York' }];
}
</script>
<p>Bitew w kronice: {battleLog.length}</p>
<button onclick={addBattle}>Dopisz bitwę</button>Użyj $state.raw dla:
- Dużych zbiorów danych
- Danych tylko do odczytu
- Optymalizacji wydajności
Kod do tej lekcji: App.svelte
<script>
// $state - deep reactivity
let warrior = $state({
name: 'Ragnar',
stats: {
health: 100,
strength: 95,
energy: 80
},
inventory: ['Axe', 'Shield']
});
function takeDamage() {
// Nested mutations work!
warrior.stats.health -= 10;
}
function heal() {
warrior.stats.health = Math.min(100, warrior.stats.health + 20);
}
function addItem(item) {
warrior.inventory.push(item);
}
</script>
<main class="warrior-panel">
<h1>{warrior.name}</h1>
<section class="stats">
<h2>Stats</h2>
<div class="stat">
<span class="stat-label">Health</span>
<div class="bar">
<div class="fill health" style="width: {warrior.stats.health}%"></div>
</div>
<span>{warrior.stats.health}/100</span>
</div>
<div class="stat">
<span class="stat-label">Strength</span>
<div class="bar">
<div class="fill strength" style="width: {warrior.stats.strength}%"></div>
</div>
<span>{warrior.stats.strength}/100</span>
</div>
<div class="actions">
<button onclick={takeDamage} class="damage">Take damage</button>
<button onclick={heal} class="heal">Heal</button>
</div>
</section>
<section class="inventory">
<h2>Inventory ({warrior.inventory.length})</h2>
<ul>
{#each warrior.inventory as item}
<li>{item}</li>
{/each}
</ul>
<button onclick={() => addItem('Sword')}>+ Add Sword</button>
</section>
</main>
<style>
.warrior-panel {
font-family: 'Georgia', serif;
max-width: 500px;
margin: 0 auto;
padding: 2rem;
background: #1a0f0a;
color: #e8d5b7;
min-height: 100vh;
}
h1 {
color: #d4af37;
text-align: center;
}
h2 {
color: #d4af37;
font-size: 1rem;
margin-bottom: 1rem;
}
section {
background: rgba(212, 175, 55, 0.1);
border: 1px solid #d4af37;
border-radius: 8px;
padding: 1rem;
margin-bottom: 1rem;
}
.stat {
margin-bottom: 0.75rem;
}
.stat .stat-label {
display: block;
margin-bottom: 0.25rem;
}
.bar {
height: 20px;
background: rgba(0,0,0,0.5);
border-radius: 4px;
overflow: hidden;
}
.fill {
height: 100%;
transition: width 0.3s;
}
.fill.health { background: linear-gradient(90deg, #8b0000, #ff4444); }
.fill.strength { background: linear-gradient(90deg, #d4af37, #ffd700); }
.actions {
display: flex;
gap: 0.5rem;
margin-top: 1rem;
}
button {
flex: 1;
padding: 0.5rem;
border: none;
border-radius: 4px;
cursor: pointer;
color: white;
}
.damage { background: #8b0000; }
.heal { background: #2d5016; }
ul {
list-style: none;
padding: 0;
}
li {
padding: 0.5rem;
background: rgba(0,0,0,0.3);
margin: 0.25rem 0;
border-radius: 4px;
}
</style>Hasła z dokumentacji w tej lekcji
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 stworzyć reaktywny stan w Svelte 5?
2. Co jest prawdą o $state w Svelte 5?
Zadania praktyczne w grze
- Edytor kodu
Uzupełnij kod tworząc reaktywną tablicę wojowników