Kurs Svelte i SvelteKit · Moduł 3: Reaktywność i runy

$derived - Wartości pochodne

2 min czytania ·
W tej lekcji5

$derived tworzy wartość, która automatycznie aktualizuje się gdy zmieniają się jej zależności. To jak runa, która reaguje na inne runy!

Podstawowe użycie

<script>
  let warriors = $state(10);
  let ships = $state(2);

  // Automatycznie się aktualizuje
  let totalPower = $derived(warriors * 10 + ships * 50);
  let armyStatus = $derived(
    totalPower > 200 ? 'Potężna armia!' : 'Potrzeba więcej wojowników'
  );
</script>

<p>Wojownicy: {warriors}, Statki: {ships}</p>
<p>Siła: {totalPower}</p>
<p>Status: {armyStatus}</p>

$derived vs $state

<script>
  let a = $state(5);
  let b = $state(3);

  // NIE rób tego - ręczna synchronizacja
  let sumWrong = $state(a + b);
  // sumWrong nie zaktualizuje się gdy zmieni się a lub b!

  // Użyj $derived
  let sumRight = $derived(a + b);
  // sumRight zawsze będzie aktualne!
</script>

Złożone obliczenia

<script>
  let items = $state([
    { name: 'Miecz', value: 100, count: 2 },
    { name: 'Tarcza', value: 50, count: 5 },
    { name: 'Hełm', value: 75, count: 3 }
  ]);

  // Obliczenia na tablicy
  let totalItems = $derived(
    items.reduce((sum, item) => sum + item.count, 0)
  );

  let totalValue = $derived(
    items.reduce((sum, item) => sum + item.value * item.count, 0)
  );

  let mostValuable = $derived(
    items.reduce((max, item) =>
      item.value > max.value ? item : max
    )
  );
</script>

<p>Przedmioty: {totalItems}</p>
<p>Wartość: {totalValue} złota</p>
<p>Najcenniejszy: {mostValuable.name}</p>

$derived.by - Złożona logika

Dla bardziej złożonych obliczeń użyj $derived.by:

<script>
  let warriors = $state([
    { name: 'Ragnar', strength: 95, isAlive: true },
    { name: 'Bjorn', strength: 88, isAlive: true },
    { name: 'Ivar', strength: 70, isAlive: false }
  ]);

  // Złożona logika z wieloma krokami
  let battleReport = $derived.by(() => {
    const alive = warriors.filter(w => w.isAlive);
    const totalStrength = alive.reduce((sum, w) => sum + w.strength, 0);
    const avgStrength = alive.length > 0
      ? totalStrength / alive.length
      : 0;

    return {
      aliveCount: alive.length,
      totalStrength,
      avgStrength: Math.round(avgStrength),
      strongest: alive.sort((a, b) => b.strength - a.strength)[0]?.name ?? 'Brak'
    };
  });
</script>

<div class="report">
  <h3>Raport bojowy</h3>
  <p>Żywych: {battleReport.aliveCount}</p>
  <p>Łączna siła: {battleReport.totalStrength}</p>
  <p>Średnia siła: {battleReport.avgStrength}</p>
  <p>Najsilniejszy: {battleReport.strongest}</p>
</div>

Łańcuch derived

<script>
  let baseValue = $state(10);

  // Każdy derived zależy od poprzedniego
  let doubled = $derived(baseValue * 2);      // 20
  let withBonus = $derived(doubled + 5);       // 25
  let final = $derived(withBonus * 1.1);       // 27.5
</script>

Wartości pochodne tworzą łańcuch magii run!

Kod do tej lekcji: App.svelte
<script>
  // $derived - derived values

  let items = $state([
    { name: 'Sword', value: 100, count: 2 },
    { name: 'Shield', value: 50, count: 5 },
    { name: 'Helmet', value: 75, count: 3 }
  ]);

  // Simple derived
  let totalItems = $derived(
    items.reduce((sum, i) => sum + i.count, 0)
  );

  let totalValue = $derived(
    items.reduce((sum, i) => sum + i.value * i.count, 0)
  );

  // $derived.by for complex logic
  let report = $derived.by(() => {
    const total = items.reduce((sum, i) => sum + i.count, 0);
    const value = items.reduce((sum, i) => sum + i.value * i.count, 0);
    const mostValuable = items.reduce((max, i) =>
      i.value > max.value ? i : max
    );

    return {
      total,
      value,
      average: total > 0 ? Math.round(value / total) : 0,
      mostValuable: mostValuable.name
    };
  });

  function addItem() {
    items.push({ name: 'Axe', value: 80, count: 1 });
  }
</script>

<main class="treasury">
  <h1>Treasury</h1>

  <section>
    <h2>Items</h2>
    <table>
      <thead>
        <tr>
          <th>Item</th>
          <th>Value</th>
          <th>Quantity</th>
          <th>Total</th>
        </tr>
      </thead>
      <tbody>
        {#each items as item}
          <tr>
            <td>{item.name}</td>
            <td>{item.value}</td>
            <td>{item.count}</td>
            <td>{item.value * item.count}</td>
          </tr>
        {/each}
      </tbody>
    </table>
    <button onclick={addItem}>+ Add Axe</button>
  </section>

  <section class="summary">
    <h2>Summary ($derived)</h2>
    <div class="stats">
      <div class="stat">
        <span class="label">Items</span>
        <span class="value">{totalItems}</span>
      </div>
      <div class="stat">
        <span class="label">Value</span>
        <span class="value">{totalValue}</span>
      </div>
    </div>
  </section>

  <section class="report">
    <h2>Report ($derived.by)</h2>
    <p>Total: <strong>{report.total}</strong> items</p>
    <p>Value: <strong>{report.value}</strong></p>
    <p>Average: <strong>{report.average}</strong>/pc</p>
    <p>Most valuable: <strong>{report.mostValuable}</strong></p>
  </section>
</main>

<style>
  .treasury {
    font-family: 'Georgia', serif;
    max-width: 600px;
    margin: 0 auto;
    padding: 2rem;
    background: #1a0f0a;
    color: #e8d5b7;
  }

  h1, h2 {
    color: #d4af37;
  }

  section {
    background: rgba(212, 175, 55, 0.1);
    border: 1px solid #d4af37;
    border-radius: 8px;
    padding: 1rem;
    margin-bottom: 1rem;
  }

  table {
    width: 100%;
    border-collapse: collapse;
    margin-bottom: 1rem;
  }

  th, td {
    padding: 0.5rem;
    text-align: left;
    border-bottom: 1px solid #3d2914;
  }

  th {
    color: #d4af37;
  }

  button {
    padding: 0.5rem 1rem;
    background: #3d2914;
    color: #e8d5b7;
    border: 1px solid #d4af37;
    border-radius: 4px;
    cursor: pointer;
  }

  .stats {
    display: flex;
    gap: 2rem;
  }

  .stat {
    text-align: center;
  }

  .stat .label {
    display: block;
    color: #8b7355;
    font-size: 0.9rem;
  }

  .stat .value {
    font-size: 1.5rem;
    color: #d4af37;
  }

  strong {
    color: #d4af37;
  }
</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. Do czego służy $derived?

  2. 2. Kiedy użyć $derived.by zamiast $derived?

Zadania praktyczne w grze

  • Edytor kodu

    Uzupełnij kod, aby doubled było podwojoną wartością count

Przydatne artykuły