$effect と Svelte 5 のライフサイクル: 使用する場合 (および使用しない場合)
Svelte 5 のルーンの中で、他のルーンよりも誤解され、悪用されているルーンが 1 つあるとしたら、それは $effect。
React から来ています。 useEffect そして多くの場合、それぞれの釘にハンマーが必要です。
Svelte でそのパターンを再現してみてください。しかし $effect 異なる哲学を持っており、
使用範囲が狭くなり、これを無視すると、フレームワークの最悪のアンチパターンに直接つながります。
このガイドでは、その内容を正確に定義しています $effect、その追跡メカニズムと同様に
自動は内部で動作しますが、それを使用するかどうかを決定するための黄金律は何ですか?
主な正当なユースケースの具体例: 分析、WebSocket、ライブラリとの統合
サードパーティと同期する localStorage.
$effect の黄金律
書く前に $effect「既存の状態から何かを計算しているのだろうか?」と自問してください。
答えが「はい」の場合は、使用します $derived. $effect そして実際の副作用については、次のとおりです。
UI の外の世界と対話する操作 (直接 DOM、ネットワーク、ストレージ、タイマー、
サードパーティのライブラリ)。
$effect の仕組み: 自動追跡
$effect DOM が更新された後に関数を実行します。特徴
基本的なこととそれ 実行中に読み取られるすべての $state と $derived を自動的に追跡します。
その実行 これらの依存関係のいずれかが変更されるたびに再実行されます。
依存関係を明示的に宣言する必要はありません。
<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>
追跡メカニズムは、同じプロキシ システムを使用します。 $state: 実行中
の $effect、リアクティブ値への各アクセスがログに記録されます。いつでも
これらの値が変化すると、エフェクトは「ダーティ」としてマークされ、再再生がスケジュールされます。
クリーンアップ機能
$effect の前に実行されるクリーンアップ関数を返すことができます。
次にエフェクトが再実行されるとき、およびコンポーネントが解体されるとき。にとって不可欠なものです
タイマー、サブスクリプション、イベント リスナーを使用してメモリ リークを回避します。
<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 変更、Svelte 5 がクリーンアップ (古い WS 接続を閉じる)、
その後、エフェクトを再実行します (新しい URL で新しい接続を開きます)。そしてきれいなパターン
手動によるライフサイクル管理は必要ありません。
ライフサイクル: $effect 対 onMount 対 onDestroy
Svelte 5 は Svelte 4 のライフサイクル機能を維持します (onMount, onDestroy、
など)下位互換性のために使用されますが、Rune はより均一なアプローチを提供します。
<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>
$effect の正当な使用例
主なシナリオは次のとおりです。 $effect そして正しい選択:
1.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. 分析と追跡
<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. サードパーティの DOM ライブラリとの統合
<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. 交差点オブザーバー (遅延読み込み、スクロール時のアニメーション)
<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>
アンチパターン: 計算に $effect を使用しない
最も一般的なアンチパターンは、 $effect それを使用して、他のものから派生した状態を更新します
状態。これにより、非効率的で無限の更新サイクルが発生する可能性があります。
<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>
$effect と無限ループ
典型的な間違いとその範囲内での編集 $effect 効果が示す $states の 1 つ
同じトラックで無限ループを作成します。
// CICLO INFINITO: count tracciato, count modificato
$effect(() => {
console.log(count); // traccia count
count++; // modifica count -> ri-esegue l'effect
});
Svelte 5 はループを検出し、開発中にエラーをスローします。 $effect から $state を変更する必要がある場合は、
使う untrack() トレースせずに読むか、デザインを再考するか。
$effect.root と $effect.pre
Svelte 5 は、次の 2 つの特殊なバージョンを提供します。 $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>
$effect を使用したコードのテスト
コンポーネントをテストする $effect 注意が必要です: 効果はスケジュールされています
非同期なので、テストは更新サイクルが完了するまで待つ必要があります。
// 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');
});
決定: $effect か $derived?
簡略化された意思決定図:
- 「既存の状態から値を計算する必要がありますか?」 →使用する
$derived - 「副作用があっても手術しなければならないのですか?」 →使用する
$effect - 「DOM を直接更新する必要がありますか?」 →使用する
$effect(というかアクションスヴェルト) - 「状態が変化したときに API 呼び出しを行うべきですか?」 →使用する
$effect - 「変換されたデータを UI に表示する必要がありますか?」 →使用する
$derived
結論と次のステップ
$effect 強力かつ特殊です。Svelte の応答性の高い UI を
外部の世界 — DOM、ネットワーク、ストレージ、サードパーティのライブラリ。に属する計算に使用します。
ある $derived 不必要な複雑さと潜在的なバグが発生します。覚えるべきパターン:
UI に表示する値を生成する場合、e $derived;それが何かと相互作用する場合
UI の外側、e $effect.
次の記事では、 SvelteKit 詳細: 負荷の仕組み サーバー側のデータ取得関数、SSR ストリーミングによる TTFB の削減方法、およびフォームの作成方法 アクションは、クライアント側の JavaScript を使用せずに突然変異を処理します。
シリーズ: Svelte 5 およびフロントエンド コンパイラー駆動
- 記事 1: コンパイラ主導のアプローチとメンタルモデル
- 記事 2: $state と $derived — ルーンとの普遍的な反応性
- 第 3 条 (本): $effect とライフサイクル — いつ使用するか (いつ使用しないのか)
- 第 4 条: SvelteKit SSR、ストリーミングおよびロード機能
- 第 5 条: Svelte 5 のトランジションとアニメーション
- 第 6 条: Svelte のアクセシビリティ: コンパイラーの警告とベスト プラクティス
- 第 7 条: グローバル状態管理: コンテキスト、ルーン、ストア
- 第 8 条: Svelte 4 から Svelte 5 への移行 — 実践ガイド
- 第 9 条: Svelte 5 でのテスト: Vitest、テスト ライブラリ、および Playwright







