Kurs Svelte i SvelteKit · Moduł 6: Logika szablonów

{#key} i {@html}

2 min czytania ·
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. 1. Kiedy używać bloku {#key}?

  2. 2. Dlaczego {@html} jest niebezpieczne?

Przydatne artykuły