Kurs Svelte i SvelteKit · Moduł 6: Logika szablonów
{#key} i {@html}
W tej lekcji4
{#key} - Wymuszanie re-renderowania
<script>
let userId = $state(1);
</script>
<!-- Komponent zostanie zniszczony i utworzony na nowo gdy zmieni się userId -->
{#key userId}
<UserProfile id={userId} />
{/key}
<button onclick={() => userId++}>Następny użytkownik</button>Przydatne dla:
- Resetowania stanu komponentu
- Ponownego uruchamiania animacji
- Wymuszania nowego fetch
{@html} - Surowy HTML
<script>
let sagaContent = $state('<strong>Ragnar</strong> wyruszył na <em>wielką wyprawę</em>.');
</script>
<!-- Renderuje HTML bez escapowania -->
{@html sagaContent}UWAGA: Używaj tylko z zaufanymi danymi! Możliwe ataki XSS.
<script>
// NIEBEZPIECZNE - dane od użytkownika
let userInput = '<img src="x" onerror="alert(1)">';
</script>
{@html userInput} <!-- NIE RÓB TEGO! -->Wstawiony tak obrazek nie istnieje, więc przeglądarka uruchamia kod z onerror - napastnik wykonuje własny skrypt na Twojej stronie.
{@const} - Lokalne stałe
{#each warriors as warrior}
{@const fullTitle = `${warrior.rank} ${warrior.name}`}
{@const isElite = warrior.strength > 80}
<div class:elite={isElite}>
<h3>{fullTitle}</h3>
</div>
{/each}{@debug} - Debugowanie
<script>
let warriors = $state([
{ id: 1, name: 'Ragnar' },
{ id: 2, name: 'Bjorn' }
]);
let selectedId = $state(null);
</script>
<!-- Zatrzymuje wykonanie gdy zmienią się wartości -->
{@debug warriors, selectedId}
{#each warriors as warrior}
<p>{warrior.name}</p>
{/each}W kodzie skryptu podobnie działa runa $inspect(warriors, selectedId) - wypisuje wartości w konsoli przy każdej zmianie.
Kod do tej lekcji: App.svelte
<script>
// {#await} - handling Promises
// $state: assigning a new Promise re-renders the {#await} block
let promise = $state(null);
function fetchWarrior() {
promise = new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.3) {
resolve({
name: 'Ragnar Lothbrok',
title: 'King of Kattegat',
strength: 95,
wisdom: 88
});
} else {
reject(new Error('The warrior did not answer the call!'));
}
}, 1500);
});
}
function reset() {
promise = null;
}
</script>
<main class="async-demo">
<h1>⏳ {"{#await}"} - Async</h1>
<p class="subtitle">Handling Promises in templates</p>
<section>
<h2>Summon a warrior</h2>
<div class="buttons">
<button onclick={fetchWarrior}>Summon</button>
<button onclick={reset}>Reset</button>
</div>
<div class="result">
{#if promise === null}
<p class="idle">Click "Summon" to call a warrior</p>
{:else}
{#await promise}
<div class="loading">
<div class="spinner"></div>
<p>Warrior is arriving...</p>
</div>
{:then warrior}
<div class="success">
<h3>{warrior.name}</h3>
<p class="title">{warrior.title}</p>
<div class="stats">
<div class="stat">
<span>Strength</span>
<strong>{warrior.strength}</strong>
</div>
<div class="stat">
<span>Wisdom</span>
<strong>{warrior.wisdom}</strong>
</div>
</div>
</div>
{:catch error}
<div class="error">
<p>{error.message}</p>
<button onclick={fetchWarrior}>Try again</button>
</div>
{/await}
{/if}
</div>
</section>
<section class="syntax">
<h2>Syntax</h2>
<pre>{"{#await promise}"}
Loading...
{"{:then data}"}
{"{data}"}
{"{:catch error}"}
{"{error.message}"}
{"{/await}"}</pre>
</section>
</main>
<style>
.async-demo {
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;
}
.subtitle {
text-align: center;
color: #8b7355;
}
section {
background: rgba(212, 175, 55, 0.1);
border: 1px solid #d4af37;
border-radius: 8px;
padding: 1rem;
margin: 1rem 0;
}
h2 {
color: #d4af37;
font-size: 1rem;
margin-top: 0;
}
.buttons {
display: flex;
gap: 0.5rem;
margin-bottom: 1rem;
}
button {
padding: 0.5rem 1rem;
background: #3d2914;
color: #e8d5b7;
border: 1px solid #d4af37;
border-radius: 4px;
cursor: pointer;
}
.result {
min-height: 150px;
padding: 1rem;
background: rgba(0,0,0,0.3);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
}
.idle {
color: #8b7355;
font-style: italic;
}
.loading {
text-align: center;
}
.spinner {
width: 40px;
height: 40px;
border: 3px solid #3d2914;
border-top-color: #d4af37;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 1rem;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.success {
text-align: center;
width: 100%;
}
.success h3 {
color: #ffd700;
margin: 0;
}
.title {
color: #8b7355;
font-style: italic;
margin: 0.25rem 0;
}
.stats {
display: flex;
justify-content: center;
gap: 2rem;
margin-top: 1rem;
}
.stat {
text-align: center;
}
.stat span {
display: block;
font-size: 0.85rem;
color: #8b7355;
}
.stat strong {
font-size: 1.5rem;
color: #d4af37;
}
.error {
text-align: center;
color: #ff6b6b;
}
.syntax pre {
background: rgba(0,0,0,0.5);
padding: 1rem;
border-radius: 4px;
font-family: monospace;
font-size: 0.9rem;
overflow-x: auto;
}
</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. Kiedy używać bloku {#key}?
2. Dlaczego {@html} jest niebezpieczne?