$efect și ciclul de viață în Svelte 5: când să îl utilizați (și când să nu îl folosiți)
Dacă există o Rună a Sveltei 5 care este înțeleasă greșit și abuzată mai mult decât celelalte, aceasta este $effect.
Venind de la React, unde useEffect și adesea ciocanul pentru fiecare cui, și natural
încercați să replici acel model în Svelte. Dar $effect are o altă filozofie, a
domeniu de utilizare mai restrâns, iar ignorarea acestuia duce direct la cele mai proaste anti-modele ale cadrului.
Acest ghid definește exact ceea ce face $effect, ca și mecanismul său de urmărire
automat funcționează intern, care este regula de aur pentru a decide dacă să-l folosești sau nu și oferă
exemple concrete ale principalelor cazuri legitime de utilizare: analytics, WebSockets, integrare cu biblioteci
terță parte și sincronizați cu localStorage.
Regula de aur a $efectului
Înainte de a scrie $effect, întreabă-te: „calculez ceva din starea existentă?”
Dacă răspunsul este da, folosește $derived. $effect și pentru efecte secundare reale:
operațiuni care interacționează cu lumea din afara interfeței de utilizare (DOM direct, rețea, stocare, temporizator,
biblioteci terțe).
Cum funcționează $effect: Urmărire automată
$effect execută o funcție după ce DOM-ul a fost actualizat. Caracteristica
fundamentale şi că urmărește automat fiecare $state și $derived pe care le citește în timpul
executarea acestuia și re-execută de fiecare dată când una dintre aceste dependențe se modifică.
Nu este necesar să declarați dependențele în mod explicit.
<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>
Mecanismul de urmărire utilizează același sistem Proxy ca și $state: în timpul execuţiei
de $effect, fiecare acces la o valoare reactivă este înregistrat. Când vreunul
dintre aceste valori se modifică, efectul este marcat ca „murdar” și programat pentru reluare.
Funcția de curățare
$effect poate returna o funcție de curățare care rulează înainte de
următoarea reexecuție a efectului și când componenta este demontată. Este esentiala pentru
evitați scurgerile de memorie cu cronometre, abonamente și ascultători de evenimente:
<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 modificări, Svelte 5 curăță (închide vechea conexiune WS),
apoi reexecută efectul (deschide o nouă conexiune cu noua adresă URL). Și un model curat
care nu necesită management manual al ciclului de viață.
Ciclul de viață: $effect vs onMount vs onDestroy
Svelte 5 menține funcțiile ciclului de viață ale Svelte 4 (onMount, onDestroy,
etc.) pentru compatibilitate inversă, dar Runes oferă o abordare mai uniformă:
<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>
Cazuri de utilizare legitime de $efect
Iată principalele scenarii în care $effect si alegerea corecta:
1. Sincronizare cu 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. Analytics și urmărire
<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. Integrare cu biblioteci DOM terțe
<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. Observator de intersecție (încărcare leneră, animații pe defilare)
<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: nu utilizați $efect pentru calcule
Cel mai comun anti-model cu $effect și folosiți-l pentru a actualiza starea derivată din altceva
stare. Acest lucru creează cicluri de actualizare ineficiente și potențial infinite:
<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>
$efect și bucle infinite
O greșeală clasică și editare în interior $effect una din $state ca efectul
aceeași pistă, creând o buclă infinită:
// CICLO INFINITO: count tracciato, count modificato
$effect(() => {
console.log(count); // traccia count
count++; // modifica count -> ri-esegue l'effect
});
Svelte 5 detectează bucle și aruncă o eroare în dezvoltare. Dacă trebuie să modificați $state dintr-un $efect,
folosi untrack() pentru a citi fără urmări sau a regândi designul.
$effect.root și $efect.pre
Svelte 5 oferă două variante specializate de $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>
Cod de testare cu $efect
Testați componentele cu $effect necesită atenție: efectele sunt programate
asincron, deci testele trebuie să aștepte finalizarea ciclului de actualizare:
// 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');
});
Decizie: $efect sau $derivat?
Diagrama de decizie simplificată:
- „Ar trebui să calculez o valoare din starea existentă?” → folosiți
$derived - „Trebuie să fac o operație cu efecte secundare?” → folosiți
$effect - „Ar trebui să actualizez DOM-ul direct?” → folosiți
$effect(sau mai degrabă, o acțiune Svelte) - „Ar trebui să fac un apel API când starea se schimbă?” → folosiți
$effect - „Ar trebui să arăt datele transformate în interfața de utilizare?” → folosiți
$derived
Concluzii și pașii următori
$effect și puternic, dar specializat: este folosit pentru a conecta interfața de utilizare receptivă a lui Svelte la
lume externă — DOM, rețea, stocare, biblioteci terță parte. Folosiți-l pentru calcule care aparțin
a $derived introduce complexitate inutilă și erori potențiale. Modelul de memorat:
dacă produce o valoare de afișat în UI, de ex $derived; dacă interacționează cu ceva
în afara interfeței de utilizare, de ex $effect.
Următorul articol se adresează SvelteKit în detaliu: cum funcționează încărcăturile funcții pentru preluarea datelor de pe partea serverului, modul în care fluxul SSR reduce TTFB și modul în care se formează acțiunile gestionează mutațiile fără JavaScript la nivel client.
Seria: Svelte 5 și Frontend Compiler-Driven
- Articolul 1: Abordare condusă de compilator și model mental
- Articolul 2: $state și $derived — Reactivitate universală cu rune
- Articolul 3 (acesta): $efect și ciclul de viață - Când să-l utilizați (și când să nu)
- Articolul 4: SvelteKit SSR, Streaming și Funcții de încărcare
- Articolul 5: Tranziții și animații în Svelte 5
- Articolul 6: Accesibilitate în Svelte: Avertismente ale compilatorului și cele mai bune practici
- Articolul 7: Managementul global al statului: context, rune și magazine
- Articolul 8: Migrarea de la Svelte 4 la Svelte 5 — Ghid practic
- Articolul 9: Testarea în Svelte 5: Vitest, Biblioteca de testare și dramaturg







