SPAモードとデータ無効化
SvelteKitのSPAモードとデータ無効化システムについて詳しく解説します。これらの機能により、高度にインタラクティブなアプリケーションと効率的なデータ管理が実現できます。
CSR/SPAモードのデータフロー
ssr = false
を設定すると、SvelteKitは完全にクライアントサイドで動作するSPAモードになります。この場合、データフローが根本的に変わり、すべての処理がブラウザ上で実行されます。
ダイアグラムを読み込み中...
SPAモードの設定
// +layout.ts または +page.ts
export const ssr = false; // サーバーサイドレンダリングを無効化
export const csr = true; // クライアントサイドレンダリングは有効(デフォルト)
export const prerender = false; // プリレンダリングも無効化
// この設定により
// 1. Server Load(+page.server.ts)は実行されない
// 2. Universal Load(+page.ts)はクライアントでのみ実行
// 3. 初回アクセスでも空のHTMLシェルのみ返される
typescript
SPAモードの動作フロー
CSR/SPAモードでは、初回アクセスも含めてすべてのデータ取得がクライアントサイドで行われます。サーバーは最小限のHTMLとJavaScriptバンドルを返すだけで、実際のコンテンツはブラウザ上で構築されます。
// SPAモードのデータフロー
// 1. ブラウザがページにアクセス
// 2. サーバーから空のHTMLシェル + JSバンドル
// 3. JavaScriptが実行される
// 4. クライアントでLoad関数実行
// 5. APIからデータ取得
// 6. DOMを構築・表示
// +page.ts(クライアントでのみ実行)
export const load: PageLoad = async ({ fetch }) => {
// このコードはブラウザでのみ実行される
const response = await fetch('/api/posts');
const posts = await response.json();
// Server Loadは存在しないため、dataパラメータは空
return { posts };
};
typescript
通常モードとSPAモードの比較
項目 | 通常モード(SSR有効) | SPAモード(ssr = false ) |
---|---|---|
初回表示速度 | 高速(完成したHTML) | 遅い(JS実行後に表示) |
SEO | 優れている | 弱い(コンテンツが空) |
Server Load | 実行される | 実行されない |
Universal Load初回 | サーバーで実行 | クライアントで実行 |
データ取得タイミング | サーバーで事前取得 | クライアントで後から取得 |
認証チェック | サーバーで可能 | クライアントのみ |
SPAモードが適している場面
// 管理画面やダッシュボード(SEO不要、インタラクティブ)
// src/routes/(admin)/+layout.ts
export const ssr = false;
export const prerender = false;
// この設定により
// - 管理画面全体がSPAとして動作
// - 高度なインタラクションが可能
// - しかしSEOは犠牲になる
typescript
SPAモードの注意点
SPAモードでは以下の制限があります。
- Server Load関数(
+page.server.ts
)は一切実行されない - 初回表示が遅くなる(JavaScriptのダウンロード・実行を待つ必要)
- SEOに大きな影響(検索エンジンはコンテンツを認識できない)
- 機密情報の扱いに注意(すべてがクライアントコードに含まれる)
データの無効化と再取得
SvelteKitは、特定のデータのみを無効化して再取得する仕組みを提供しています。これにより、画面全体をリロードすることなく、必要な部分のみを更新できます。
ダイアグラムを読み込み中...
invalidate()の使い方
invalidate()
関数を使用すると、特定の条件に一致するLoad関数を再実行できます。これは、データの更新後や定期的なリフレッシュに便利です。
重要な動作原理
invalidate()
を実行すると、
- 指定したURLまたは識別子に依存するLoad関数が再実行される
- Load関数が新しいデータを返す
- コンポーネントの
$props()
で受け取るデータが自動的に更新される - Svelteのリアクティビティにより、UIが自動的に再レンダリングされる
// +page.svelte
<script lang="ts">
import { invalidate, invalidateAll } from '$app/navigation';
import type { PageData } from './$types';
// Load関数から取得したデータを$propsで受け取る
let { data }: { data: PageData } = $props();
// data.userが自動的に更新される
// 特定URLのデータを無効化
async function refreshUserData() {
// /api/userをfetchしているすべてのLoad関数が再実行される
await invalidate('/api/user');
// この後、data.userは新しい値に自動更新される
// 手動でdataを更新する必要はない!
}
// カスタム識別子による無効化
async function refreshCustomData() {
// 'custom:data'に依存するLoad関数のみ再実行
await invalidate('custom:data');
// 該当するデータが自動的に更新される
}
// 全データを無効化
async function refreshAll() {
// ページ内のすべてのLoad関数を再実行
await invalidateAll();
// data全体が新しい値で置き換わる
}
</script>
<!-- UIは自動的に更新される -->
<p>現在のユーザー: {data.user.name}</p>
<!-- 使用例:データ更新ボタン -->
<button onclick={refreshUserData}>
ユーザー情報を更新
</button>
typescript
ダイアグラムを読み込み中...
depends()による依存関係の登録
depends()
関数を使用して、Load関数にカスタムの依存関係を登録できます。これにより、特定の識別子でのみ無効化される、より細かい制御が可能になります。
なぜdepends()が必要?
- fetch()を使う場合: URLへの依存が自動登録される
- fetch()を使わない場合: 手動で
depends()
を使って依存関係を登録する必要がある - カスタム制御: 複数のLoad関数を同じタイミングで更新したい場合
識別子の命名について
depends()
に渡す識別子('app:posts'
、'custom:data'
など)は完全に任意の文字列です。
- URLである必要はない(ただしURLも使える)
app:posts
は単なるラベル(/api/posts
とは無関係)- 慣習として
app:
、custom:
、data:
などのプレフィックスを使うことが多い - 重要なのは
invalidate()
で使う文字列と一致すること - わかりやすい名前を付けることが大切(例:
user:profile
、cart:items
) - 1つのLoad関数で複数の識別子を登録可能(複数回
depends()
を呼べる)
実践的な例:投稿とコメントの連動更新
// +layout.ts - サイドバーの人気投稿
export const load: LayoutLoad = async ({ fetch, depends }) => {
// 任意の識別子で依存関係を登録(URLではない!)
depends('app:posts'); // "投稿関連のデータ"という意味の任意のラベル
// データベースから直接取得(fetchを使わない)
const popularPosts = await getPopularPostsFromDB();
return { popularPosts };
};
typescript
// +page.ts - メインコンテンツの投稿一覧
export const load: PageLoad = async ({ fetch, depends }) => {
// 同じ識別子を登録(layout.tsと連動させるため)
depends('app:posts'); // この文字列はlayout.tsと同じならなんでもOK
// APIから投稿を取得
const posts = await fetch('/api/posts').then(r => r.json());
// fetch()を使うと'/api/posts'への依存も自動登録される
// つまりこのLoad関数は2つの識別子に依存
// 1. 'app:posts' (手動登録)
// 2. '/api/posts' (自動登録)
return { posts };
};
typescript
// +page.server.ts - 複数の識別子を登録する例
export const load: PageServerLoad = async ({ depends, locals }) => {
// 複数の識別子を登録可能
depends('app:posts'); // 投稿更新時に再実行
depends('user:profile'); // ユーザー情報更新時に再実行
depends('app:settings'); // 設定変更時に再実行
// 条件によって追加の識別子を登録
if (locals.user?.isAdmin) {
depends('admin:dashboard'); // 管理者なら管理画面更新時も再実行
}
// このLoad関数は以下のいずれかで再実行される
// - invalidate('app:posts')
// - invalidate('user:profile')
// - invalidate('app:settings')
// - invalidate('admin:dashboard') (管理者の場合)
// - invalidateAll()
const data = await getComplexData();
return { data };
};
typescript
// +page.svelte - UIから更新
<script lang="ts">
import { invalidate } from '$app/navigation';
async function createPost() {
// 新しい投稿を作成
await fetch('/api/posts', {
method: 'POST',
body: JSON.stringify(newPost)
});
// 任意の識別子'app:posts'に依存するすべてのLoad関数を再実行
await invalidate('app:posts');
// → layout.tsとpage.tsの両方が再実行される(両方にdepends('app:posts')があるため)
// → サイドバーとメインコンテンツが同時に更新される
}
async function refreshOnlyAPI() {
// URL '/api/posts'に依存するLoad関数のみ再実行
await invalidate('/api/posts');
// → page.tsのみ再実行される(fetch('/api/posts')を使っているため)
// → layout.tsは再実行されない('/api/posts'をfetchしていないため)
}
async function refreshOnlyCustom() {
// もし他の任意の識別子を使った場合
await invalidate('user:settings');
// → depends('user:settings')を登録したLoad関数のみ再実行
// → 'app:posts'とは無関係なので、投稿関連は更新されない
}
</script>
typescript
動作の流れ(シーケンス図)
ダイアグラムを読み込み中...
depends()の使い分け
ケース | 方法 | 再実行される範囲 |
---|---|---|
fetch()使用 | 自動登録 | そのURLをfetchしているLoad関数のみ |
depends('custom:id'') | 手動登録 | 同じ識別子を登録したすべてのLoad関数 |
invalidateAll() | - | ページ内のすべてのLoad関数 |
まとめ
SvelteKitのSPAモードとデータ無効化システムにより以下が実現できます。
SPAモードの特徴
- 完全なクライアントサイドレンダリング - サーバーは最小限のHTMLシェルのみ
- 高度なインタラクティビティ - フルSPAとしての機能
- 既存APIとの統合 - レガシーシステムとの親和性
- オフライン対応 - Service Workerとの組み合わせ
データ無効化の利点
- 細粒度の更新制御 - 必要な部分のみを効率的に更新
- 自動リアクティビティ -
$props()
と連携した自動UI更新 - 柔軟な依存関係管理 - URLとカスタム識別子の組み合わせ
- パフォーマンス最適化 - 全画面リロードの回避
これらの機能を適切に活用することで、モダンで高性能なWebアプリケーションを構築できます。
次のステップ
- ストリーミングSSR - 段階的なコンテンツ配信
- フォーム処理とActions - データの送信と処理
- アーキテクチャ詳解 - システム全体の動作原理