S SvelteKurs
M2 · Runes & Reaktivität

$derived — abgeleitete Werte

Werte, die sich aus anderem State ergeben: einmal deklariert, immer aktuell — Svelte berechnet sie automatisch neu, wenn sich eine Abhängigkeit ändert.

Wozu abgeleiteter Zustand?

Oft ergibt sich ein Wert direkt aus einem anderen: der doppelte Zähler, der Gesamtpreis eines Warenkorbs, ob ein Formular gültig ist. Solche Werte willst du nicht selbst pflegen — $derived übernimmt das. Du beschreibst nur wie sich der Wert berechnet, und Svelte hält ihn automatisch aktuell.

Zaehler.svelte svelte
<script>
  let count = $state(0);
  let doubled = $derived(count * 2);
</script>

<button onclick={() => count++}>
  {count} verdoppelt ist {doubled}
</button>

doubled wird nie von Hand gesetzt. Ändert sich count, berechnet Svelte doubled neu — und aktualisiert jede Stelle im Markup, die es verwendet.

Automatisches Dependency-Tracking

Das Besondere: Du musst nirgends angeben, wovon ein abgeleiteter Wert abhängt. Svelte liest den Ausdruck aus und merkt sich automatisch jeden $state, der darin gelesen wird. Wird eine dieser Quellen geändert, wird neu berechnet — sonst nicht.

js
let count = $state(0);

// Jeder abgeleitete Wert deklariert seine Abhängigkeit einfach dadurch,
// dass er sie im Ausdruck liest — kein manuelles Verdrahten:
let doubled = $derived(count * 2);
let isEven  = $derived(count % 2 === 0);
let label   = $derived(isEven ? 'gerade' : 'ungerade');

Abgeleitete Werte dürfen sich auch aufeinander stützen: label hängt von isEven ab, das wiederum von count. Svelte löst diese Kette in der richtigen Reihenfolge auf.

Live ausprobieren

Der folgende Zähler nutzt einen $state und zwei $derived-Werte:

▶ $derived-Zähler · Interaktiv
0
doubled
0
isEven
gerade ✅

Nur count wird geklickt — doubled und isEven folgen von allein.

$derived.by für mehr Logik

$derived(ausdruck) erwartet einen einzelnen Ausdruck. Sobald du mehrere Zeilen, Zwischenschritte oder eine Schleife brauchst, nimmst du $derived.by(() => { … }) — eine Funktion, deren Rückgabewert der abgeleitete Wert ist. Das Dependency-Tracking funktioniert genauso automatisch.

js
let zahlen = $state([10, 20, 30]);

// Für mehrzeilige Logik: $derived.by mit einer Funktion.
let statistik = $derived.by(() => {
  const summe = zahlen.reduce((a, b) => a + b, 0);
  const schnitt = zahlen.length ? summe / zahlen.length : 0;
  return { summe, schnitt };
});
▶ Statistik mit $derived.by · Interaktiv
102030
Anzahl
3
Summe
60
Ø
20.0

Warum nicht einfach eine Funktion?

Man könnte den abgeleiteten Wert auch als Funktion schreiben und im Markup aufrufen. Das funktioniert — ist aber schlechter. Eine Funktion rechnet bei jedem Render erneut, auch wenn sich nichts Relevantes geändert hat. Ein $derived ist gecacht: Es merkt sich das Ergebnis und berechnet nur dann neu, wenn sich eine Abhängigkeit tatsächlich ändert.

svelte
<script>
  let count = $state(0);

  // ❌ Funktion: läuft bei JEDEM Render erneut, kein Caching
  function doubledFn() {
    return count * 2;
  }

  // ✅ $derived: gecacht, läuft nur neu, wenn count sich ändert
  let doubled = $derived(count * 2);
</script>

<p>{doubledFn()}</p>  <!-- rechnet jedes Mal -->
<p>{doubled}</p>      <!-- liest nur den Cache -->

💡 Faustregel

Hängt ein Wert von reaktivem State ab und wird im Markup angezeigt? Dann nimm $derived statt einer Funktion. Das ist ausdrucksstärker und schneller.

Ein $derived darf man nicht zuweisen

Ein abgeleiteter Wert gehört Svelte — er ergibt sich vollständig aus seinen Quellen. Deshalb darfst du ihm nicht direkt einen Wert zuweisen. Willst du das Ergebnis ändern, änderst du die zugrunde liegende Quelle.

js
let count = $state(0);
let doubled = $derived(count * 2);

doubled = 99; // ❌ Fehler: einem $derived darf man nicht zuweisen
count = 49;   // ✅ so beeinflusst man doubled — über die Quelle

⚠️ Kein Setter

Eine Zuweisung an ein $derived ist ein Fehler. Denk an einen abgeleiteten Wert wie an eine Formel in einer Tabellenkalkulation: Du änderst die Eingabezellen, nicht das Ergebnisfeld.

Rails-Vergleich

In Rails erreichst du Ähnliches mit Memoization (@x ||= …): Ein Wert wird einmal berechnet und gecacht. Der Unterschied liegt in der Invalidierung.

🛤️ Rails-Vergleich: Memoization vs. $derived

Rails-Memoization cacht, aber invalidiert nie von allein — ändert sich eine Eingabe, bleibt der Cache veraltet, bis du ihn manuell zurücksetzt. $derived verfolgt seine Abhängigkeiten automatisch und berechnet exakt dann neu, wenn nötig.

Rails ruby
class Invoice < ApplicationRecord
  # Memoization: berechnet EINMAL, dann gecacht in @total
  def total
    @total ||= line_items.sum(&:amount)
  end
end

# Problem: Ändert sich ein line_item, ist @total veraltet.
# Der Cache invalidiert NICHT von allein — du musst @total = nil
# selbst zurücksetzen. Genau das nimmt dir $derived ab.

Zusammenfassung

💡 Das Wichtigste

  • $derived(ausdruck) deklariert einen Wert, der aus anderem State folgt.
  • $derived.by(() => { … }) für mehrzeilige Logik.
  • Abhängigkeiten werden automatisch erkannt — kein manuelles Verdrahten.
  • Das Ergebnis ist gecacht und aktualisiert sich nur bei Bedarf.
  • Einem $derived darf man nicht zuweisen — ändere die Quelle.

🎯 Probier es selbst

Ergänze die Statistik-Demo um einen weiteren $derived-Wert maximum, der die größte Zahl der Liste anzeigt (Tipp: Math.max(...zahlen)). Beobachte, dass er sich ohne weiteres Zutun aktualisiert, sobald du eine Zahl hinzufügst.