$efekt a životní cyklus ve Svelte 5: Kdy jej použít (a kdy ne)
Pokud existuje jedna Rune of Svelte 5, která je nepochopena a zneužívána více než ostatní, je to $effect.
Pochází z Reactu, kde useEffect a často kladivo na každý hřebík, a přírodní
zkuste replikovat tento vzor ve Svelte. Ale $effect má jinou filozofii, a
užší rozsah použití a jeho ignorování vede přímo k nejhorším anti-vzorcům rámce.
Tato příručka přesně definuje, co dělá $effect, stejně jako jeho sledovací mechanismus
automatika funguje interně, jaké je zlaté pravidlo pro rozhodování, zda ji použít nebo ne, a poskytuje
konkrétní příklady hlavních legitimních případů použití: analytika, WebSockets, integrace s knihovnami
třetí strany a synchronizovat se s localStorage.
Zlaté pravidlo $efektu
Před psaním $effect, zeptejte se sami sebe: "Počítám něco z existujícího stavu?"
Pokud je odpověď ano, použijte $derived. $effect a pro skutečné vedlejší účinky:
operace, které interagují se světem mimo uživatelské rozhraní (přímý DOM, síť, úložiště, časovač,
knihovny třetích stran).
Jak $efekt funguje: Automatické sledování
$effect spustí funkci po aktualizaci DOM. Funkce
základní a to automaticky sleduje každý $stav a $derived, který během něj přečte
jeho provedení a znovu se spustí pokaždé, když se jedna z těchto závislostí změní.
Závislosti není nutné explicitně deklarovat.
<script lang="ts">
let count = $state(0);
let name = $state('Federico');
// Questo effect dipende da ENTRAMBI count e name
// perche li legge entrambi durante l'esecuzione
$effect(() => {
console.log(`${name} ha un contatore di ${count}`);
// Si ri-esegue quando count O name cambiano
});
// Questo effect dipende SOLO da count
$effect(() => {
document.title = `Contatore: ${count}`;
// Si ri-esegue SOLO quando count cambia
});
</script>
Sledovací mechanismus používá stejný proxy systém jako $state: během provádění
z $effect, je zaprotokolován každý přístup k reaktivní hodnotě. Když nějaký
těchto hodnot se změní, efekt je označen jako „špinavý“ a naplánován na opětovné přehrání.
Funkce čištění
$effect může vrátit funkci čištění, která běží před
další opětovné provedení efektu a při demontáži součásti. Je nezbytné pro
vyhněte se únikům paměti pomocí časovačů, předplatných a posluchačů událostí:
<script lang="ts">
let wsUrl = $state('wss://api.example.com/stream');
let messages = $state<string[]>([]);
$effect(() => {
// Effect traccia wsUrl: si ri-esegue se l'URL cambia
const ws = new WebSocket(wsUrl);
ws.addEventListener('message', (event) => {
messages.push(event.data);
});
ws.addEventListener('error', (error) => {
console.error('WebSocket error:', error);
});
// Cleanup: chiude la connessione prima della prossima esecuzione
// e quando il componente viene smontato
return () => {
ws.close();
console.log('WebSocket chiuso');
};
});
</script>
Se wsUrl změny, Svelte 5 vyčistí (uzavře staré připojení WS),
poté znovu spustí efekt (otevře nové připojení s novou adresou URL). A čistý vzor
který nevyžaduje manuální správu životního cyklu.
Životní cyklus: $effect vs onMount vs onDestroy
Svelte 5 zachovává funkce životního cyklu Svelte 4 (onMount, onDestroy,
atd.) pro zpětnou kompatibilitu, ale runy nabízejí jednotnější přístup:
<script lang="ts">
import { onMount, onDestroy } from 'svelte'; // Svelte 4 style
let data = $state(null);
// --- Svelte 4 pattern ---
onMount(async () => {
data = await fetchData();
});
// --- Svelte 5 equivalente con $effect ---
$effect(() => {
// $effect.root per effects che non dipendono da stato
// e si eseguono una sola volta (equivalente di onMount)
fetchData().then(result => {
data = result;
});
return () => {
// cleanup equivalente a onDestroy
};
});
// --- Svelte 5 per operazioni truly una-tantum ---
// Preferisci il blocco di codice nel componente
// o usa untrack() per evitare tracking accidentale
import { untrack } from 'svelte';
$effect(() => {
// untrack impedisce il tracking di initialValue
const initial = untrack(() => data);
console.log('Valore iniziale (non tracciato):', initial);
// count E tracciato perche e fuori da untrack
console.log('Count corrente:', count);
});
</script>
Legitimní případy použití $efektu
Zde jsou hlavní scénáře, ve kterých $effect a správná volba:
1. Synchronizace s localStorage
<script lang="ts">
// Leggi il valore iniziale da localStorage (fuori da $effect)
let theme = $state<'light' | 'dark'>(
(localStorage.getItem('theme') as 'light' | 'dark') ?? 'light'
);
// Sincronizza le modifiche a localStorage
$effect(() => {
localStorage.setItem('theme', theme);
document.documentElement.setAttribute('data-theme', theme);
});
</script>
<button onclick={() => theme = theme === 'light' ? 'dark' : 'light'}>
Toggle theme
</button>
2. Analýza a sledování
<script lang="ts">
const { pageId, userId }: { pageId: string; userId?: string } = $props();
// Traccia la visualizzazione di pagina quando pageId cambia
$effect(() => {
// Sia pageId che userId sono tracciati
analytics.track('page_view', {
page: pageId,
user: userId ?? 'anonymous',
timestamp: new Date().toISOString()
});
});
</script>
3. Integrace s knihovnami DOM třetích stran
<script lang="ts">
import Chart from 'chart.js/auto';
let canvasEl: HTMLCanvasElement;
let chartData = $state({ labels: [], datasets: [] });
let chartInstance: Chart | null = null;
$effect(() => {
// Prima esecuzione: crea il chart
if (!chartInstance) {
chartInstance = new Chart(canvasEl, {
type: 'bar',
data: chartData
});
} else {
// Ri-esecuzioni: aggiorna i dati
chartInstance.data = chartData;
chartInstance.update();
}
return () => {
// Cleanup: distruggi il chart
chartInstance?.destroy();
chartInstance = null;
};
});
</script>
<canvas bind:this={canvasEl}></canvas>
4. Intersection Observer (Lazy Loading, Animace on Scroll)
<script lang="ts">
let element: HTMLElement;
let isVisible = $state(false);
let hasBeenVisible = $state(false);
$effect(() => {
const observer = new IntersectionObserver(
(entries) => {
isVisible = entries[0].isIntersecting;
if (isVisible) hasBeenVisible = true;
},
{ threshold: 0.1 }
);
observer.observe(element);
return () => observer.disconnect();
});
</script>
<div
bind:this={element}
class:visible={isVisible}
class:animated={hasBeenVisible}
>
Contenuto che appare con animazione
</div>
Anti-Pattern: Nepoužívejte $efekt pro výpočty
Nejběžnější anti-vzor s $effect a použijte jej k aktualizaci stavu odvozeného od něčeho jiného
stát. To vytváří neefektivní a potenciálně nekonečné cykly aktualizací:
<script lang="ts">
let items = $state([1, 2, 3, 4, 5]);
let total = $state(0); // SBAGLIATO: non dovrebbe essere $state
// ANTI-PATTERN: usare $effect per calcolare un valore derivato
$effect(() => {
total = items.reduce((s, i) => s + i, 0);
// Questo crea: items cambia -> effect corre -> total cambia ->
// potenziali altri effects si ri-eseguono inutilmente
});
// CORRETTO: usare $derived
const totalCorrect = $derived(items.reduce((s, i) => s + i, 0));
</script>
<script lang="ts">
let searchQuery = $state('');
let filteredUsers = $state([]); // SBAGLIATO
// ANTI-PATTERN: filtrare con $effect
$effect(() => {
filteredUsers = users.filter(u =>
u.name.toLowerCase().includes(searchQuery.toLowerCase())
);
});
// CORRETTO: $derived
const filteredUsersCorrect = $derived(
users.filter(u =>
u.name.toLowerCase().includes(searchQuery.toLowerCase())
)
);
</script>
$efekt a Infinite Loops
Klasická chyba a úprava uvnitř $effect jeden z $ uvádí, že efekt
stejná stopa, vytvářející nekonečnou smyčku:
// CICLO INFINITO: count tracciato, count modificato
$effect(() => {
console.log(count); // traccia count
count++; // modifica count -> ri-esegue l'effect
});
Svelte 5 detekuje smyčky a vyvolá chybu ve vývoji. Pokud potřebujete upravit $state z $efektu,
použití untrack() číst bez sledování nebo přehodnotit design.
$effect.root a $effect.pre
Svelte 5 nabízí dvě specializované varianty $effect:
<script lang="ts">
// $effect.pre: esegue PRIMA dell'aggiornamento del DOM
// Utile per leggere il DOM prima che venga modificato
// (es: salvare la posizione di scroll prima di un aggiornamento)
$effect.pre(() => {
const scrollPos = window.scrollY;
return () => {
// Ripristina scroll dopo l'aggiornamento del DOM
window.scrollTo(0, scrollPos);
};
});
// $effect.root: crea un effetto che non e legato al ciclo di vita
// del componente, utile fuori dai componenti
const cleanup = $effect.root(() => {
$effect(() => {
// Effect che persiste oltre la distruzione del componente
// (utile per singleton e stores globali)
});
return () => { /* cleanup manuale */ };
});
</script>
Testovací kód s $efektem
Testujte komponenty s $effect vyžaduje pozornost: účinky jsou naplánovány
asynchronně, takže testy musí počkat na dokončení cyklu aktualizace:
// test/MyComponent.test.ts
import { render } from '@testing-library/svelte';
import { tick } from 'svelte';
import MyComponent from './MyComponent.svelte';
test('$effect sincronizza con localStorage', async () => {
const { getByRole } = render(MyComponent);
const button = getByRole('button', { name: 'Toggle theme' });
button.click();
// Aspetta che il ciclo di aggiornamento Svelte completi
await tick();
expect(localStorage.getItem('theme')).toBe('dark');
});
Rozhodnutí: $efekt nebo $derived?
Zjednodušený rozhodovací diagram:
- "Mám vypočítat hodnotu z existujícího stavu?" → použití
$derived - "Musím podstoupit operaci s vedlejšími účinky?" → použití
$effect - "Mám aktualizovat DOM přímo?" → použití
$effect(nebo spíše akční Svelte) - "Mám provést volání API, když se změní stav?" → použití
$effect - "Mám zobrazit transformovaná data v uživatelském rozhraní?" → použití
$derived
Závěry a další kroky
$effect a výkonný, ale specializovaný: používá se k připojení responzivního uživatelského rozhraní Svelte k
vnější svět — DOM, síť, úložiště, knihovny třetích stran. Použijte jej pro výpočty, které patří
a $derived přináší zbytečnou složitost a potenciální chyby. Vzor k zapamatování:
pokud vytvoří hodnotu pro zobrazení v uživatelském rozhraní, např $derived; pokud s něčím interaguje
mimo uživatelské rozhraní, např $effect.
Další článek řeší SvelteKit podrobně: jak fungují zátěže funkce pro načítání dat na straně serveru, jak streamování SSR snižuje TTFB a jak formuláře akce zpracovávají mutace bez JavaScriptu na straně klienta.
Řada: Svelte 5 a Frontend Compiler-Driven
- Článek 1: Přístup řízený kompilátorem a mentální model
- Článek 2: $state a $derived — Univerzální reaktivita s runami
- Článek 3 (tento): $efekt a životní cyklus – kdy jej používat (a kdy ne)
- Článek 4: SvelteKit SSR, funkce streamování a načítání
- Článek 5: Přechody a animace v Svelte 5
- Článek 6: Přístupnost ve Svelte: Varování a osvědčené postupy kompilátoru
- Článek 7: Globální správa státu: kontext, runy a obchody
- Článek 8: Přechod ze Svelte 4 na Svelte 5 – Praktický průvodce
- Článek 9: Testování ve Svelte 5: Vitest, testovací knihovna a dramatik







