$state ve $derived: Svelte 5 Runes ile Evrensel Reaktivite
Yanıt verme sistemi, herhangi bir modern kullanıcı arayüzü çerçevesinin kalbidir. Svelte 5 tamamen yeniden tasarlandı
Svelte 4'ün "sihirli görevi"nden hareketle reaktivitenin nasıl çalıştığı Rünler: işlevler
ön ek ile özel $ Derleyiciyle doğrudan iletişim kuran. İki temel Rün
ben $state e $derivedyanıt verme kullanım durumlarının %90'ını kapsar
herhangi bir Svelte uygulaması.
Bu kılavuz, bu iki Rünün dahili olarak nasıl çalıştığını, neden ES6 Proxy'yi kullandıklarını açıklamaktadır.
derin tepkime, mesela $derived otomatik notlandırmayı uygular ve - dahası
Önemli — bunların normal TypeScript modüllerinde .svelte dosyalarının dışında nasıl kullanılabileceği,
tamamen yeni devlet yönetimi modelleri.
Önkoşullar
- Svelte 5 ile ilgili temel bilgiler (bkz. makale 1: Derleyici Odaklı Yaklaşım)
- Orta Düzey TypeScript (jenerikler, Proxy, alıcı/ayarlayıcı)
- Notlandırma ve bağımlılık izleme konsepti
$state: ES6 Proxy ile reaktif durum
Svelte 4'te blokta bildirilen herhangi bir değişken <script> otomatik olarak oldu
reaktif: derleyici her atamayı takip etti ve güncelleme kodunu oluşturdu. Bu işe yaradı
ilkel türler, ancak nesneler ve diziler için sınırlamaları vardı: "derin" değişiklikler
array.push() o obj.nested.prop = value takip edilmediler.
Svelte 5 bunu şu şekilde çözüyor: $state, kullandığı ES6 Proxy'si herkesi engellemek
iç içe olanlar da dahil olmak üzere nesne erişimi ve değişikliği. Sonuç gerçek derin tepkiselliktir:
<script lang="ts">
// Tipi primitivi: reattivita diretta
let count = $state(0);
let name = $state('Federico');
let isVisible = $state(true);
// Oggetti: reattivita profonda tramite Proxy
let user = $state({
name: 'Federico',
address: {
city: 'Bari',
country: 'IT'
},
tags: ['developer', 'writer']
});
function updateCity() {
// Questo funziona! La modifica profonda e tracciata
user.address.city = 'Milano';
}
function addTag(tag: string) {
// Anche push() e tracciato grazie al Proxy
user.tags.push(tag);
}
</script>
<p>Citta: {user.address.city}</p>
<p>Tags: {user.tags.join(', ')}</p>
Svelte 4 ile, user.address.city = 'Milano' bir kullanıcı arayüzü güncellemesini tetiklemezdi
çünkü derleyici atamaları yalnızca en üst düzey değişkene çizdi (user = ...).
Svelte 5 ve Proxy ile herhangi bir derinlik seviyesindeki tüm erişimler engellenir.
$state Proxy Dahili Olarak Nasıl Çalışır?
Derin yanıt verme hızını anlamak için ES6 Proxy'nin nasıl çalıştığını ve Svelte'nin onu nasıl kullandığını anlamanız gerekir:
// Implementazione concettuale del Proxy usato da $state
// (semplificata rispetto al codice reale di Svelte 5)
function createReactiveProxy<T extends object>(
target: T,
onChange: () => void
): T {
return new Proxy(target, {
get(obj, prop) {
const value = Reflect.get(obj, prop);
// Se il valore e un oggetto/array, crea un Proxy annidato
if (value !== null && typeof value === 'object') {
return createReactiveProxy(value, onChange);
}
return value;
},
set(obj, prop, value) {
const result = Reflect.set(obj, prop, value);
// Notifica gli osservatori che qualcosa e cambiato
onChange();
return result;
}
});
}
Temel olarak, yazarken user.address.city = 'Milano', şu olur:
- Harici proxy müdahaleleri
.addressve iç içe geçmiş bir Proxy döndürüraddress - Yuvalanmış Proxy müdahaleleri
.city = 'Milano' - Değeri güncelleyin ve Svelte'nin yanıt verme sistemini bilgilendirin
- Svelte yalnızca aşağıdakilere bağlı bağlamalar için bir güncelleme planlar:
user.address.city
Proxy ÇALIŞMADIĞINDA
Proxy aracılığıyla yanıt vermenin beklendiği gibi çalışmadığı durumlar vardır:
-
Yıkım yanıt verme yeteneğini kaybeder:
const { city } = user.address;—cityve şimdi ilkel bir dize, vekil değil. Her zaman kullanuser.address.citydoğrudan şablonda. -
Yayılma reaktiviteyi kaybeder:
const copy = { ...user };reaktif olmayan bir kopya oluşturur. -
Harita ve Ayar, özel diziler için $state.raw gerektirir:
Tüm egzotik koleksiyon yöntemleri yamalı değildir. Harita/Set için şunu kullanın:
$state(new Map())— Hızlı 5 bunlara da yama yapın.
TypeScript ile $state: Tam Yazma
$state ve TypeScript ile tamamen entegredir. Tür otomatik olarak çıkarılır
başlangıç değerinden, ancak jeneriklerle açık olabilirsiniz:
<script lang="ts">
// Inferenza automatica del tipo
let count = $state(0); // number
let name = $state(''); // string
let flag = $state(false); // boolean
// Tipo esplicito per casi complessi
interface User {
id: number;
name: string;
role: 'admin' | 'user';
metadata: Record<string, unknown>;
}
let user = $state<User>({
id: 1,
name: 'Federico',
role: 'admin',
metadata: {}
});
// Array con tipo esplicito
let items = $state<string[]>([]);
// Stato nullable (inizializzato a null)
let selectedItem = $state<User | null>(null);
// $state.raw: reattivita superficiale (solo l'assegnamento, non le mutazioni)
// Utile per grandi array dove la performance conta
let bigList = $state.raw<number[]>([]);
function addToList(n: number) {
// Con $state.raw, devi riassegnare (non push)
bigList = [...bigList, n];
}
</script>
$türetilmiş: Otomatik Depolama
$derived yalnızca bağımlılıkları değiştiğinde yeniden hesaplanan hesaplanmış değerler oluşturur.
Ve eşdeğeri useMemo React'in, ancak bağımlılıkları açıkça beyan etmek zorunda kalmadan -
derleyici, işlev kodunu analiz ederek bunları otomatik olarak izler.
<script lang="ts">
let items = $state([
{ id: 1, name: 'Laptop', price: 1200, category: 'tech' },
{ id: 2, name: 'Mouse', price: 25, category: 'tech' },
{ id: 3, name: 'Desk', price: 450, category: 'furniture' }
]);
let filterCategory = $state('');
let sortBy = $state<'name' | 'price'>('name');
let sortAsc = $state(true);
// $derived traccia automaticamente: items, filterCategory, sortBy, sortAsc
const filteredAndSorted = $derived(() => {
let result = filterCategory
? items.filter(i => i.category === filterCategory)
: [...items];
result.sort((a, b) => {
const mult = sortAsc ? 1 : -1;
if (sortBy === 'name') return a.name.localeCompare(b.name) * mult;
return (a.price - b.price) * mult;
});
return result;
});
// Dipende solo da filteredAndSorted
const totalValue = $derived(
filteredAndSorted.reduce((sum, item) => sum + item.price, 0)
);
// Dipende da items direttamente
const categories = $derived([...new Set(items.map(i => i.category))]);
</script>
Svelte 5 şunu kullanır: itme-çekme sistemi not alma için: bir bağımlılık değiştiğinde,
$derived "kirli" (itme) olarak işaretlenir, ancak değer hemen yeniden hesaplanmaz.
Yalnızca birisi onu okuduğunda (çektiğinde) yeniden hesaplanır. Türetileni kimse okumazsa hesaplama gerçekleşmez.
$derived.by: Fonksiyonlu Karmaşık Hesaplamalar
Birden fazla ifade gerektiren hesaplamalar için şunu kullanın: $derived.by() hangisini kabul ediyor
gövdeli bir fonksiyon:
<script lang="ts">
let transactions = $state([
{ amount: 100, type: 'income', date: new Date('2026-01-15') },
{ amount: -50, type: 'expense', date: new Date('2026-01-16') },
{ amount: 200, type: 'income', date: new Date('2026-02-01') },
{ amount: -75, type: 'expense', date: new Date('2026-02-15') }
]);
// Per computazioni multi-step, usa $derived.by
const stats = $derived.by(() => {
const income = transactions
.filter(t => t.type === 'income')
.reduce((sum, t) => sum + t.amount, 0);
const expenses = transactions
.filter(t => t.type === 'expense')
.reduce((sum, t) => sum + Math.abs(t.amount), 0);
const balance = income - expenses;
const byMonth = transactions.reduce((acc, t) => {
const key = t.date.toISOString().slice(0, 7); // YYYY-MM
acc[key] = (acc[key] ?? 0) + t.amount;
return acc;
}, {} as Record<string, number>);
return { income, expenses, balance, byMonth };
});
</script>
<p>Saldo: {stats.balance}</p>
<p>Entrate: {stats.income} — Uscite: {stats.expenses}</p>
TypeScript Dosyalarındaki Rünler: Devrimci Haberler
Svelte 4 ile Svelte 5 arasındaki en önemli fark, Rünlerin söz dizimi değil, Rünler .svelte.ts veya .svelte.js uzantılı tüm dosyalarda çalışırsadece bu değil bileşenlerde. Bu tamamen yeni durum yönetimi modellerine olanak tanır.
// src/lib/stores/cart.svelte.ts
// Un "store" costruito con i Runes, senza dipendenze da Svelte stores
export interface CartItem {
id: number;
name: string;
price: number;
quantity: number;
}
function createCart() {
let items = $state<CartItem[]>([]);
// Computati derivati dallo stato del carrello
const total = $derived(
items.reduce((sum, item) => sum + item.price * item.quantity, 0)
);
const itemCount = $derived(
items.reduce((sum, item) => sum + item.quantity, 0)
);
const isEmpty = $derived(items.length === 0);
function addItem(item: Omit<CartItem, 'quantity'>) {
const existing = items.find(i => i.id === item.id);
if (existing) {
existing.quantity++; // Mutazione diretta: il Proxy la traccia
} else {
items.push({ ...item, quantity: 1 });
}
}
function removeItem(id: number) {
const index = items.findIndex(i => i.id === id);
if (index !== -1) items.splice(index, 1);
}
function updateQuantity(id: number, quantity: number) {
const item = items.find(i => i.id === id);
if (item) {
if (quantity <= 0) removeItem(id);
else item.quantity = quantity;
}
}
function clear() {
items = [];
}
return {
// Esponi come getter per proteggere l'accesso diretto
get items() { return items; },
get total() { return total; },
get itemCount() { return itemCount; },
get isEmpty() { return isEmpty; },
addItem,
removeItem,
updateQuantity,
clear
};
}
// Singleton: un'istanza condivisa tra tutti i componenti che importano questo modulo
export const cart = createCart();
Bu mağaza herhangi bir .svelte bileşenine aktarılabilir ve doğrudan kullanılabilir. reaktivite:
<!-- CartSummary.svelte -->
<script lang="ts">
import { cart } from '$lib/stores/cart.svelte';
</script>
{#if cart.isEmpty}
<p>Il carrello e vuoto</p>
{:else}
<p>{cart.itemCount} prodotti — Totale: €{cart.total.toFixed(2)}</p>
{#each cart.items as item (item.id)}
<div>
<span>{item.name}</span>
<button onclick={() => cart.updateQuantity(item.id, item.quantity - 1)}>-</button>
<span>{item.quantity}</span>
<button onclick={() => cart.updateQuantity(item.id, item.quantity + 1)}>+</button>
<button onclick={() => cart.removeItem(item.id)}>Rimuovi</button>
</div>
{/each}
<button onclick={cart.clear}>Svuota carrello</button>
{/if}
TypeScript Sınıflarında $state
Runes ayrıca TypeScript sınıflarında da çalışarak durum yönetimi için OOP tarzı bir model sağlar:
// src/lib/models/todo-list.svelte.ts
export class TodoList {
// I campi della classe usano $state come inizializzatore
items = $state<{ id: number; text: string; done: boolean }[]>([]);
filter = $state<'all' | 'active' | 'done'>('all');
// $derived come getter calcolato
readonly filtered = $derived.by(() => {
switch (this.filter) {
case 'active': return this.items.filter(i => !i.done);
case 'done': return this.items.filter(i => i.done);
default: return this.items;
}
});
readonly stats = $derived.by(() => ({
total: this.items.length,
done: this.items.filter(i => i.done).length,
active: this.items.filter(i => !i.done).length
}));
private nextId = 1;
add(text: string) {
this.items.push({ id: this.nextId++, text, done: false });
}
toggle(id: number) {
const item = this.items.find(i => i.id === id);
if (item) item.done = !item.done;
}
delete(id: number) {
this.items = this.items.filter(i => i.id !== id);
}
clearDone() {
this.items = this.items.filter(i => !i.done);
}
}
// Puo essere usata come singleton o istanziata per componente
export const todoList = new TodoList();
Hesaplanan React useMemo ve Vue ile karşılaştırma
Otomatik bağımlılık takibi $derived hataların ana kaynağını ortadan kaldırır
eşdeğer React ve Vue çözümlerinde:
// React: dipendenze manuali, fonte di bug
const filteredItems = useMemo(() => {
return items.filter(i => i.category === filter);
}, [items, filter]); // Se dimentichi una dipendenza: bug silenzioso
// Vue 3: tracking automatico, ma sintassi verbosa
const filteredItems = computed(() => {
return items.value.filter(i => i.category === filter.value);
});
// Svelte 5: tracking automatico, sintassi diretta
const filteredItems = $derived(
items.filter(i => i.category === filter)
);
Pratik fark: React ile dizideki bağımlılığı unutun useMemo üretir
sessiz eski değerler, hata ayıklamanın oldukça zor olduğu bilinen bir hata. Svelte 5'te bu sorun yok
çünkü izleme çalışma zamanında Proxy aracılığıyla gerçekleşir.
$state ve $derived ne zaman kullanılır?
- $state'i kullan kullanıcı eylemlerine veya olaylarına yanıt olarak değişen değerler için (giriş alanları, geçiş anahtarları, API'den yüklenen veriler)
- $derived'ı kullan mevcut $state'den hesaplanabilen herhangi bir şey için (toplamlar, filtreler, biçimlendirme, dönüşümler)
-
Hesaplamalar için $ effect kullanmayın — eğer kendinizi yazarken bulursanız
$effect(() => { derived = compute(state); }), ABD$derivedYerine
Gelişmiş Model: Paylaşılan Reaktivite Bağlamı
Karmaşık uygulamalarda ve paylaşılan tepkisel bir "bağlam" yaratmada çok yararlı bir model bir hiyerarşideki bileşenler arasında:
// src/lib/context/theme.svelte.ts
import { getContext, setContext } from 'svelte';
const THEME_KEY = Symbol('theme');
export function createThemeContext() {
let isDark = $state(
typeof window !== 'undefined'
? localStorage.getItem('theme') === 'dark'
: false
);
const theme = $derived(isDark ? 'dark' : 'light');
const cssClass = $derived(`theme-${theme}`);
$effect(() => {
if (typeof window !== 'undefined') {
localStorage.setItem('theme', theme);
document.documentElement.className = cssClass;
}
});
function toggle() { isDark = !isDark; }
function setDark(value: boolean) { isDark = value; }
const context = { get isDark() { return isDark; }, get theme() { return theme; }, toggle, setDark };
setContext(THEME_KEY, context);
return context;
}
export function useTheme() {
return getContext<ReturnType<typeof createThemeContext>>(THEME_KEY);
}
Sonuçlar ve Sonraki Adımlar
$state e $derived bunlar Svelte 5'teki tepkiselliğin iki temel direğidir.
.svelte bileşenlerinin dışında çalışabilme yeteneği, Svelte 4'ün sunduğu mimari olasılıkların önünü açıyor
izin vermedi: modüler durum yönetimi, izolasyonda test edilebilir mantık, reaktif OOP kalıpları
açık Bağlam API'leri olmadan bileşenler arasında entegre ve paylaşım durumu.
Bir sonraki makale inceliyor $efekt — Yan etkiler için Rune — ve ne zaman yapılacağını açıklıyor
doğru şekilde kullanın ve bunun yerine ne zaman kullanmanız gerektiğini öğrenin $derived. Çok önemli bir konu çünkü
kötüye kullanılması $effect ve Svelte 5'teki ana anti-desen.
Seri: Svelte 5 ve Ön Uç Derleyici Tabanlı
- Madde 1: Derleyici Odaklı Yaklaşım ve Zihinsel Model
- Madde 2 (bu): $state ve $derived — Runes ile Evrensel Reaktivite
- Madde 3: $ effect ve Yaşam Döngüsü - Ne Zaman Kullanılmalı (ve Ne Zaman Kullanılmamalı)
- Madde 4: SvelteKit SSR, Akış ve Yükleme İşlevleri
- Madde 5: Svelte 5'teki Geçişler ve Animasyonlar
- Madde 6: Svelte'de Erişilebilirlik: Derleyici Uyarıları ve En İyi Uygulamalar
- Madde 7: Küresel Devlet Yönetimi: Bağlam, Rünler ve Mağazalar
- Madde 8: Svelte 4'ten Svelte 5'e Geçiş — Pratik Kılavuz
- Madde 9: Svelte 5'te Test Yapma: Vitest, Test Kütüphanesi ve Oyun Yazarı







