svelte-core-bestpractices について
Svelte を使う開発者向けに、リアクティビティ、コンポーネント構成、スタイルの実践方法をまとめたスキルです。runes モードを優先し、不要なエフェクトを避ける方針を示します。
主な機能
- 01適切なリアクティブ状態の選択
- 02$derived による値の計算
- 03不要な $effect の抑制
- 04新しいコンポーネント・イベント構文
- 05リスト描画とコンテキストの改善
活用シーン
- 01新しい Svelte 5 コンポーネントの作成
- 02リアクティビティの問題調査
- 03既存の Svelte コードの改善
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ sveltejs/ ai-tools --skill svelte-core-bestpracticesインストール後、Claude Code で/svelte-core-bestpracticesを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/attach.mdawait-expressions.mdbind.mdeach.mdhydratable.mdinspect.mdrender.mdsnippet.mdsvelte-reactivity.md詳細
$state
リアクティブであるべき変数、つまり $effect、$derived、またはテンプレート式を更新させる変数にのみ、$state ルーンを使用してください。それ以外は通常の変数でかまいません。
オブジェクトと配列($state({...}) または $state([...]))は深くリアクティブになります。つまり、変更すると更新が発生します。ただし、細かなリアクティビティと引き換えに、オブジェクトをプロキシ化する必要があり、パフォーマンスのオーバーヘッドが生じます。変更ではなく再代入だけを行う大きなオブジェクトを扱う場合は、代わりに $state.raw を使用してください。たとえば、API のレスポンスではこの方法が適していることがよくあります。
$derived
状態から何かを計算する場合は、$effect ではなく $derived を使用してください。
// do this
let square = $derived(num * num);
// don't do this
let square;
$effect(() => {
square = num * num;
});
メモ
$derivedに渡すのは関数ではなく式です。たとえば式が複雑で、関数を使う必要がある場合は、$derived.byを使用してください。
派生値には書き込みが可能です。$state と同様に代入できますが、式が変わると再評価されます。
派生式がオブジェクトまたは配列の場合、そのまま返され、深くリアクティブにはなりません。ただし、必要になるまれなケースでは、$derived.by 内で $state を使用できます。
$effect
エフェクトは最終手段であり、ほとんどの場合は避けてください。特に、エフェクト内で状態を更新するのは避けてください。
- D3 などの外部ライブラリと状態を同期する必要がある場合は、
{@attach ...}を使うほうがすっきりすることがよくあります。 - ユーザー操作に応じてコードを実行する必要がある場合は、適切にイベントハンドラーへ直接コードを記述するか、関数バインディングを使用してください。
- デバッグのために値を記録する必要がある場合は、
$inspectを使用してください。 - Svelte の外部にあるものを監視する必要がある場合は、
createSubscriberを使用してください。
エフェクトの内容を if (browser) {...} などで囲むことは絶対に避けてください。エフェクトはサーバー上では実行されません。
$props
プロパティは変化するものとして扱ってください。たとえば、プロパティに依存する値には通常 $derived を使用します。
// @errors: 2451
let { type } = $props();
// do this
let color = $derived(type === 'danger' ? 'red' : 'green');
// don't do this — `color` will not update if `type` changes
let color = type === 'danger' ? 'red' : 'green';
$inspect.trace
$inspect.trace はリアクティビティのデバッグツールです。何かが正しく更新されない場合や、必要以上に実行される場合は、$effect または $derived.by(あるいはそれらが呼び出す関数)の最初の行に $inspect.trace(label) を追加して、依存関係を追跡し、どれが更新を引き起こしたのかを確認できます。
#key ブロック
#key ブロックは、特定の式が変化したときにトランジションやアニメーションを実行するのに便利ですが、非常にコストがかかることがあります。ブロック内のすべての要素やコンポーネントが毎回アンマウントされるため、使用は控えめにしてください。たとえば、子コンポーネントの <script> ブロック内の式を再実行させるために #key を使うのではなく、それらの式を $derived で囲むことを検討してください。
イベント
on で始まる要素属性はすべて、イベントリスナーとして扱われます。
<button onclick={() => {...}}>click me</button>
<button {onclick}>...</button>
<button {...props}>...</button>
window または document にリスナーを設定する必要がある場合は、<svelte:window> と <svelte:document> を使用できます。
<svelte:window onkeydown={...} />
<svelte:document onvisibilitychange={...} />
この用途に onMount や $effect を使用するのは避けてください。
スニペット
スニペットは、{@render ...} タグでインスタンス化したり、プロパティとしてコンポーネントに渡したりできる、再利用可能なマークアップのまとまりを定義する方法です。スニペットはテンプレート内で宣言する必要があります。
{#snippet greeting(name)}
<p>hello {name}!</p>
{/snippet}
{@render greeting('world')}
メモ
コンポーネントのトップレベル(つまり、要素やブロックの内側ではない場所)で宣言されたスニペットは、
<script>内で参照できます。コンポーネントの状態を参照しないスニペットは<script module>内でも使用でき、その場合はエクスポートして他のコンポーネントから利用できます。
Each ブロック
key 付き each ブロックの使用を推奨します。既存の項目が属する DOM を更新するのではなく、Svelte が項目を挿入または削除できるため、パフォーマンスが向上します。
メモ
キーはオブジェクトを一意に識別する必要があります。インデックスをキーとして使用しないでください。
項目を変更する必要がある場合は、分割代入を避けてください(たとえば、bind:value={item.count} を使用する場合など)。
CSS で JavaScript 変数を使用する
CSS 内で使用したい JS 変数がある場合は、style: ディレクティブで CSS カスタムプロパティを設定できます。
<div style:--columns={columns}>...</div>
その後、コンポーネントの <style> 内で var(--columns) を参照できます。
子コンポーネントのスタイル設定
コンポーネントの <style> 内の CSS は、そのコンポーネントにスコープされます。親コンポーネントから子コンポーネントのスタイルを制御する必要がある場合は、CSS カスタムプロパティを使用する方法を推奨します。
<Child --color="red" />
<h1>Hello</h1>
<style>
h1 {
color: var(--color);
}
</style>
これが不可能な場合(たとえば、子コンポーネントがライブラリ由来の場合)は、:global を使ってスタイルを上書きできます。
<div>
<Child />
</div>
<style>
div :global {
h1 {
color: red;
}
}
</style>
コンテキスト
共有モジュールで状態を宣言する代わりに、コンテキストの使用を検討してください。これにより、状態を必要とするアプリの一部にスコープを限定でき、サーバーサイドレンダリング時にユーザー間で状態が漏れる可能性もなくなります。
setContext と getContext ではなく createContext を使用してください。型安全性が得られます。
非同期 Svelte
バージョン 5.36 以降を使用している場合は、await 式とハイドレーション可能を使用して、コンポーネント内でプロミスを直接扱えます。これらはまだ完全に安定しているとは見なされていないため、vite.config.js または svelte.config.js のプラグインオプションで experimental.async を有効にする必要があります。
レガシー機能を避ける
新しいコードでは常に rune モードを使用し、より新しい代替機能がある機能は避けてください。
- 暗黙的なリアクティビティ(例:
let count = 0; count += 1)の代わりに$stateを使用する $:の代入やステートメントの代わりに$derivedと$effectを使用する(ただし、より良い解決策がない場合にのみエフェクトを使用する)export let、$$props、$$restPropsの代わりに$propsを使用するon:click={...}の代わりにonclick={...}を使用する<slot>、$$slots、<svelte:fragment>の代わりに{#snippet ...}と{@render ...}を使用する<svelte:component this={DynamicComponent}>の代わりに<DynamicComponent>を使用する<svelte:self>の代わりにimport Self from './ThisComponent.svelte'と<Self>を使用する- ストアを使う代わりに、
$stateフィールドを持つクラスを使ってコンポーネント間でリアクティビティを共有する use:actionの代わりに{@attach ...}を使用するclass:ディレクティブの代わりに、class属性で clsx スタイルの配列やオブジェクトを使用する