Contents
SvelteKit プロジェクトのセットアップ(npm create svelte@latest + pnpm 推奨)
Svelte 5 で開発を始めるなら、公式が推奨するコマンド npm create svelte@latest で Vite ベースのテンプレートを生成し、依存管理は高速かつディスク使用量が小さい pnpm を使うのが最もシンプルです。
この手順を踏めば数分でローカルサーバーが立ち上がり、すぐにコンポーネント開発に移れます。
Vite テンプレート作成手順
以下は実際にターミナルで入力するコマンド例です。対話形式の質問に答えるだけでプロジェクト雛形が完成します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# 1. プロジェクト用ディレクトリを作成して移動 mkdir my-svelte-app && cd my-svelte-app # 2. 最新テンプレートを生成(Skeleton project、TypeScript、ESLint などは対話で選択) npm create svelte@latest . # 3. pnpm がインストールされていなければグローバルに導入 npm i -g pnpm # 4. パッケージをインストールし、開発サーバー起動 pnpm install pnpm dev # → http://localhost:5173 にアクセスできる |
公式ドキュメントは https://kit.svelte.dev/docs/introduction(Vite が内部で使用されていることが記載)をご参照ください。
pnpm ワークスペースと依存関係の最適化
モノレポ構成で複数の SvelteKit アプリや共有ライブラリを管理したい場合、pnpm-workspace.yaml にルートと子ディレクトリを列挙するだけで依存がフラットにまとめられます。
|
1 2 3 4 5 |
# pnpm-workspace.yaml(プロジェクトのルートに配置) packages: - "apps/*" - "libs/*" |
-
ロックファイルのフラット化
pnpm は同一バージョンのパッケージをハードリンクで共有するため、node_modulesのサイズは数十 MB に抑えられます。CI のキャッシュ容量が削減でき、ビルド時間も短縮されます。 -
実務上のベネフィット
@sveltejs/kitや共通ユーティリティを単一インストールで管理。- 各アプリの依存バージョンが揃うため、コードレビューやデプロイ時に「動作しない」リスクが低減します。
.svelte ファイルの構造とスクリプトブロックのルール
.svelte コンポーネントは \<script>・\<style>・マークアップ の三部構成で、スクリプト側には4つの基本ルールが適用されます。これらを守ることでコンパイル時に自動的にリアクティブ変数へ変換され、余計なランタイムコードが削減されます。
必須要素と任意要素
| 要素 | 用途・特徴 |
|---|---|
<script>(インスタンススコープ) |
コンポーネント内部で実行。export let による Props 定義やローカル変数、インポート文を記述します。 |
<script context="module">(モジュールスコープ) |
ビルド時に一度だけ評価され、SSR のデータ取得やユーティリティ関数の定義に利用。必須ではありませんが、サーバー側ロジックを分離したいときに便利です。 |
<style> |
デフォルトでコンポーネントスコープ化(.svelte-[hash])。:global() を使えばグローバル CSS も記述可能です。 |
| マークアップ(HTML / Svelte 構文) | 任意の HTML タグや {} バインディングを書くだけで OK。 |
スクリプトブロックに適用される4つのルール
-
import
トップレベルでのみ使用可能です。他のスコープ(例:context="module")からもインポートできますが、実行タイミングはビルド時です。 -
export let
親コンポーネントから渡される Props を宣言。型注釈を付ければ TypeScript の型チェックが走ります。 -
$(リアクティブステートメント)
$:で始まる文は依存変数の変更を検知して自動再実行されます。計算プロパティや副作用ロジックに最適です。 -
module context
context="module"のスクリプトはコンポーネント全体で一度だけ評価され、SSR 時のデータフェッチやユーティリティ関数の定義に利用します。
詳細は公式ドキュメント(https://kit.svelte.dev/docs/components)をご確認ください。
Props と型付け、リアクティブステートメント $ の活用
Svelte 5 では TypeScript が標準でサポートされているため、Props に型情報を付与すれば IDE 補完やコンパイル時チェックが自動的に有効になります。加えて $: を使ったリアクティブ宣言は依存追跡がビルトインなので、手書きの watch ロジックよりコード量が削減できます。
Props の定義方法とデフォルト値
|
1 2 3 4 5 6 7 8 9 10 |
<script lang="ts"> // 必須 Prop(型だけ宣言しデフォルトは与えない) export let count: number; // 任意 Prop(デフォルト値を設定) export let label: string = '項目'; </script> <p>{label}: {count}</p> |
- 必須は型だけを書き、呼び出し側で必ず渡す必要があります。
- 任意は
=後に初期値を入れることで、未指定時のフォールバックが自動的に適用されます。
TypeScript による Props の型注釈ベストプラクティス
複数の Props がある場合は interface または type を作成すると管理が楽になります。オプションプロパティは ? とデフォルト値で安全に扱えます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<script lang="ts"> interface CardProps { title: string; amount?: number; // 任意 onClose: () => void; } export let { title, amount = 0, onClose }: CardProps; </script> <h2>{title}</h2> {#if amount} <p>金額:{amount}円</p> {/if} <button on:click={onClose}>閉じる</button> |
interfaceにまとめておくと、他コンポーネントへの再利用やドキュメント生成が容易です。- 任意プロパティは型レベルで
?を付け、実装側ではデフォルト値を設定して「未定義」状態を排除します。
$: リアクティブ宣言と実務シナリオ
|
1 2 3 4 5 6 7 8 9 10 |
<script lang="ts"> export let basePrice: number; export let taxRate = 0.1; // 計算プロパティ:税込み金額を自動再計算 $: total = Math.round(basePrice * (1 + taxRate)); </script> <p>税込み金額: {total}円</p> |
basePriceやtaxRateが変わるたびにtotalが即座に更新され、手動で再計算ロジックを書く必要がありません。
非同期データのリアクティブ取得(Svelte 5 の新機能)
|
1 2 3 4 5 6 7 8 9 10 |
<script lang="ts"> let userId = 1; // `$:` に `await` を組み込めるので、データ取得を簡潔に記述できる $: user = await fetch(`/api/users/${userId}`).then(r => r.json()); </script> {#if user} <p>{user.name} さん、ようこそ!</p> {/if} |
awaitを含む$:は自動的に Promise の解決を待ち、結果が変わるたびに再評価されます。これにより「ロード中」や「エラーハンドリング」のロジックもシンプル化できます。
コンポーネント間通信:イベントディスパッチ・フォワードとスロット
子コンポーネントから親へデータを送る手段として createEventDispatcher、ラップコンポーネントで子の全イベントを転送する forwardEvents が標準装備されています。さらに デフォルトスロット と 名前付きスロット を組み合わせれば、汎用 UI コンテナが簡単に作れます。
createEventDispatcher と forwardEvents の実装例
子コンポーネント(Button.svelte)
|
1 2 3 4 5 6 7 8 9 10 11 |
<script lang="ts"> import { createEventDispatcher } from 'svelte'; const dispatch = createEventDispatcher<{ click: MouseEvent }>(); function handleClick(event: MouseEvent) { dispatch('click', event); // カスタムイベントを発火 } </script> <button on:click={handleClick}>クリック</button> |
ラップコンポーネント(IconButton.svelte)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<script lang="ts"> import Button from './Button.svelte'; export let icon = '🔔'; // 子の全イベントを残余プロパティと共に転送 import { forwardEvents } from 'svelte/internal'; const $$restProps = {}; forwardEvents(Button, $$restProps); </script> <Button {...$$restProps}> <span slot="default">{icon}</span> </Button> |
forwardEventsの第2引数に残余プロパティオブジェクトを渡すだけで、<IconButton on:click={...}>が子コンポーネントのクリックイベントを直接受け取れます。
スロットの種類と fallback コンテンツ
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<!-- Card.svelte --> <div class="card"> <header> <!-- 名前付きスロット(タイトル) --> <slot name="title">【タイトル未設定】</slot> </header> <section> <!-- デフォルトスロット(本文) --> <slot>コンテンツがありません。</slot> </section> <footer> <!-- 名前付きスロット(アクションボタン群) --> <slot name="actions"></slot> </footer> </div> <style> .card { border: 1px solid #ddd; padding: 1rem; } </style> |
利用側の記述例:
|
1 2 3 4 5 6 |
<Card> <h2 slot="title">商品情報</h2> 商品の詳細テキスト… <button slot="actions" on:click={addToCart}>カートに入れる</button> </Card> |
- デフォルトスロット は
<slot>の中身が fallback コンテンツとして表示されます。 - 名前付きスロット は
slot="name"属性で差し替え可能なので、レイアウトの柔軟性が向上します。
スタイリング・最適化・テスト、そして Svelte 5 の破壊的変更点
Svelte 5 では CSS がデフォルトでコンポーネントスコープ化され、:global() によってグローバルスタイルを明示的に定義できます。TailwindCSS と組み合わせると JIT ビルドの高速化が得られ、Vitest + Testing Library が公式テストフレームワークとして推奨されています。また、コンパイラオプションや SSR の挙動が大幅に刷新されたため、開発者は新しい設定項目を把握しておく必要があります。
CSS スコープ・:global と TailwindCSS 併用例
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<script lang="ts"> export let primary = false; </script> <style> /* コンポーネントスコープ */ button { @apply px-4 py-2 rounded; /* Tailwind のユーティリティを CSS 内で使用 */ background-color: var(--bg); } :global(.dark) button { --bg: #333; color: #fff; } :global(body) { margin: 0; /* グローバルに適用したいスタイルは :global() */ } </style> <button class={primary ? 'bg-blue-600 text-white' : 'bg-gray-200'}> <slot>ボタン</slot> </button> |
@applyにより Tailwind のユーティリティクラスを CSS ファイル内で直接使用でき、スコープ化されたスタイルと組み合わせても JIT が正しく働きます。:global(.dark)で外部テーマクラスと連携すれば、SSR 時に同一 CSS がサーバー側でも適用され、クライアントと見た目がずれません。
Vitest と @testing-library/svelte によるテスト環境構築
- 依存パッケージをインストール
bash
pnpm add -D vitest @testing-library/svelte @sveltejs/vite-plugin-svelte
vite.config.tsにプラグインを明示的に追加(SvelteKit では自動ですがテスト実行時は必要)
ts
import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';
export default defineConfig({
plugins: [svelte()],
test: {
environment: 'jsdom',
globals: true,
coverage: {
provider: 'c8'
}
}
});
- テスト例(Button.test.ts)
ts
import { render, fireEvent } from '@testing-library/svelte';
import Button from '../src/lib/Button.svelte';
test('クリック時にカスタムイベントが発火する', async () => {
const handle = vi.fn();
const { getByText } = render(Button, {
props: { / ここに必要なら Props を渡す / },
on: { click: handle }
});
|
1 2 3 |
await fireEvent.click(getByText('クリック')); expect(handle).toHaveBeenCalledOnce(); |
});
renderが仮想 DOM にコンポーネントをマウントし、fireEventでユーザー操作をシミュレートします。- GitHub Actions 等の CI パイプラインに組み込めば、プルリクエストごとに自動テストが走ります。
Svelte 5 のコンパイラオプションと SSR の主な変更点
| 変更項目 | 具体的内容 | 実務上の影響 |
|---|---|---|
compilerOptions.hydrate → ssr: true/false |
ビルド時に SSR 用かクライアント用かを明示的に分離。Hydration コードは SSR ビルドで自動除外される。 | 静的サイト (adapter-static) とサーバーレンダリング (adapter-node) の切り替えが設定ファイルだけで完結し、ビルドサイズが最適化されます。 |
cssHash カスタマイズの廃止 |
デフォルトハッシュ方式(.svelte-xxxx)に統一。独自ハッシュはプラグイン実装が必要に。 |
ビルドキャッシュ戦略がシンプル化し、CDN キャッシュの破棄やバージョニングが容易になります。 |
$page ストアの型定義強化 |
load 関数から返すデータは自動的に型推論され、PageData が正確に表現される。 |
手書きの型宣言が不要になるため、開発速度と安全性が向上します。 |
preprocess の設定形式変更 |
svelte.config.js の preprocess はオブジェクト形式(例: { typescript: true, postcss: true })へ統一。 |
Vite プラグインや他ツールとの競合が減り、設定ミスが起きにくくなります。 |
| モジュールスコープの自動 import | src/lib/* 配下のファイルは import $lib/... で簡略化可能($lib エイリアス)。 |
パス指定が短縮され、リファクタリング時の影響範囲が限定的です。 |
詳細は公式リリースノート(https://kit.svelte.dev/docs/migrating#svelte-5)をご確認ください。
ビルドサイズ最適化と開発ツール活用
-
コードスプリッティング
import()を使って遅延ロードコンポーネントを分割すれば、初回バンドルが小さくなります。例:const Modal = () => import('./Modal.svelte'); -
適切な Adapter の選定
- 静的サイトは
@sveltejs/adapter-static(最小バンドル) -
動的 SSR は
@sveltejs/adapter-node、Vercel デプロイは@sveltejs/adapter-vercel -
Tree‑shaking
Vite の ES モジュール解析と Svelte コンパイラのデッドコード除去により、未使用コンポーネントは自動的にバンドルから排除されます。 -
Svelte DevTools
Chrome/Firefox 拡張機能をインストールし、pnpm dev --openで起動したローカルサーバー上のコンポーネントツリーやリアクティブステートの再計算回数を可視化できます。デバッグ効率が大幅に向上します。
まとめ
- 環境構築は
npm create svelte@latestと高速な pnpm を組み合わせるだけで完了し、公式 Vite テンプレートが即座に利用可能です。 .svelteファイルは \<script>・\<style>・マークアップ の三部構成で、インポート・export let・リアクティブステートメント・モジュールコンテキストの4ルールを守ると自動的にリアクティブ化されます。- Props は TypeScript の型注釈とデフォルト値で安全に扱い、
$:を用いた計算プロパティや非同期取得はコード量削減と可読性向上に貢献します。 - カスタムイベントは createEventDispatcher、ラップ時の転送は forwardEvents、UI の再利用は デフォルト/名前付きスロット + fallback が基本です。
- スコープ化 CSS と
:global()、TailwindCSS 併用でスタイル衝突を防ぎつつ高速 UI を構築でき、テストは Vitest + @testing-library/svelte が推奨されます。 - Svelte 5 ではコンパイラオプション(
ssr/cssHash)や SSR の挙動が刷新されたため、公式リリースノートを常にチェックしながら設定を更新してください。
これらのポイントを押さえておけば、React や Vue の経験者でもスムーズに Svelte 5 に移行でき、プロジェクト全体の生産性とコード品質を向上させることが可能です。