mcpField
S
svelte-core-bestpractices

svelte-core-bestpractices

github.com/sveltejs開発33511.2k 回インストール2 日前に更新
リポジトリ

svelte-core-bestpractices について

Svelte を使う開発者向けに、リアクティビティ、コンポーネント構成、スタイルの実践方法をまとめたスキルです。runes モードを優先し、不要なエフェクトを避ける方針を示します。

主な機能

  1. 01適切なリアクティブ状態の選択
  2. 02$derived による値の計算
  3. 03不要な $effect の抑制
  4. 04新しいコンポーネント・イベント構文
  5. 05リスト描画とコンテキストの改善

活用シーン

  1. 01新しい Svelte 5 コンポーネントの作成
  2. 02リアクティビティの問題調査
  3. 03既存の Svelte コードの改善

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/sveltejs/ai-tools --skill svelte-core-bestpractices

インストール後、Claude Code で/svelte-core-bestpracticesを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
attach.md
await-expressions.md
bind.md
each.md
hydratable.md
inspect.md
render.md
snippet.md
svelte-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 スタイルの配列やオブジェクトを使用する

コメント