データフローの詳細

関連ページ

このページはLoad関数の基本的な実行順序とデータの流れを解説しています。

学習パス: 基本的な流れを理解 → 実践パターンを学ぶ → 内部実装を理解

SvelteKitのデータフローは、サーバーサイドとクライアントサイドで異なる動作をします。このページでは、Load関数の実行順序、データの流れ、並列処理の仕組みを詳しく解説します。

SSR時のデータフロー

初回アクセスやページリロード時のサーバーサイドレンダリングでは、以下の順序でデータが処理されます。SSRにより、SEOに優れた完全なHTMLがサーバー側で生成され、初回表示が高速化されます。

ダイアグラムを読み込み中...

実行順序の詳細

サーバーサイドレンダリング時のデータ取得は、効率的な並列処理と段階的な実行により最適化されています。

1. リクエスト受信

ブラウザからのHTTPリクエストをSvelteKitサーバーが受信し、URLパラメータやクエリパラメータを解析します。この段階で、どのルートにマッチするかが決定され、対応するLoad関数が特定されます。

2. Server Load関数の並列実行

レイアウトとページのServer Load関数が同時に実行されます。これにより、独立したデータ取得が並列化され、待ち時間が最小限に抑えられます。

// +layout.server.ts と +page.server.ts が同時に実行される
// お互いのデータを待つ必要がない
// 例:ユーザー情報と商品情報を並列で取得
export const load: LayoutServerLoad = async () => {
  return { user: await getUser() };  // 非同期だが他を待たない
};

export const load: PageServerLoad = async () => {
  return { products: await getProducts() };  // 上記と同時実行
};
typescript

3. Universal Load関数の実行

Server Loadの完了後、そのデータを引き継いでUniversal Load関数が実行されます。ここで追加のデータ取得や、Server Loadのデータの加工が可能です。

// Server Loadのデータを受け取って実行
export const load: PageLoad = async ({ data, fetch }) => {
  // data: Server Loadからのデータ
  // fetch: SSR対応のfetch関数(Cookie等を自動で引き継ぐ)
  const additional = await fetch('/api/extra');
  return {
    ...data,  // Server Loadのデータを展開
    additional: await additional.json()  // 追加データ
  };
};
typescript

4. HTMLレンダリングとハイドレーション

すべてのLoad関数が完了すると、サーバーで完全なHTMLが生成されます。データはJSONとしてHTMLに埋め込まれ、クライアントサイドでハイドレーションされることで、インタラクティブなアプリケーションになります。

クライアントサイドナビゲーション

一度SvelteKitアプリケーションが起動した後のページ遷移では、SPA的なクライアントサイドナビゲーションが行われます。これにより、ページ全体のリロードなしに高速な画面遷移が実現されます。

ダイアグラムを読み込み中...

キャッシュとプリフェッチ

SvelteKitは自動的にデータをキャッシュし、ユーザーがリンクにホバーした時点でデータの先読みを行うことができます。これにより、実際のクリック時には瞬時にページが表示されます。

<!-- プリフェッチ戦略の設定 -->
<!-- hover: マウスオーバー時に先読み(デフォルト) -->
<a href="/posts/123" data-sveltekit-preload-data="hover">
  記事を読む
</a>

<!-- tap: タッチ/クリック時に先読み(モバイル向け) -->
<a href="/posts/456" data-sveltekit-preload-data="tap">
  重い記事を読む
</a>

<!-- off: プリフェッチを無効化 -->
<a href="/large-page" data-sveltekit-preload-data="off">
  大きなページへ
</a>
svelte

プログラマティックにプリフェッチを制御することも可能です。

import { prefetch, prefetchRoutes } from '$app/navigation';

// 単一ページのプリフェッチ
await prefetch('/posts/123');

// 複数ページの一括プリフェッチ
await prefetchRoutes(['/about', '/contact', '/products']);
typescript

データの差分更新

特定のデータのみを無効化して再取得することで、必要最小限の更新で画面を最新化できます。これにより、パフォーマンスを維持しながらリアルタイムな情報更新が可能になります。

// 特定のデータのみ無効化
import { invalidate } from '$app/navigation';

// URLベースの無効化 - このURLに依存するLoad関数が再実行される
await invalidate('/api/user');
// 例:/api/userをfetchしているすべてのLoad関数が再実行

// カスタム識別子による無効化
await invalidate('app:user');
// depends('app:user')を宣言したLoad関数のみ再実行

// 全データの無効化 - すべてのLoad関数を再実行
import { invalidateAll } from '$app/navigation';
await invalidateAll();
// 画面全体のデータをリフレッシュ
typescript

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は、レイアウトとページのLoad関数を可能な限り並列実行することで、データ取得のパフォーマンスを最大化します。これにより、待ち時間を最小限に抑え、高速なページ表示を実現します。

ダイアグラムを読み込み中...

並列実行の実装例

複数のAPIからデータを取得する場合、Promise.allを使用することで並列処理が可能です。これにより、最も遅いAPIのレスポンス時間がボトルネックとなりますが、順次実行と比較して大幅な高速化が期待できます。

// +page.server.ts - 並列でデータ取得
export const load: PageServerLoad = async ({ fetch }) => {
  // Promise.allで3つのAPIを同時に呼び出す
  const [posts, categories, tags] = await Promise.all([
    fetch('/api/posts').then(r => r.json()),
    fetch('/api/categories').then(r => r.json()),
    fetch('/api/tags').then(r => r.json())
  ]);
  
  // 例:各APIが1秒かかる場合
  // 順次実行: 3秒
  // 並列実行: 1秒(3倍高速)
  
  return { posts, categories, tags };
};
typescript

ウォーターフォールの回避

データ取得が連鎖的に依存する場合でも、工夫により並列化を最大化できます。ウォーターフォール(順次実行)を避けることで、大幅なパフォーマンス改善が可能です。

// ❌ 悪い例:順次実行(ウォーターフォール)
export const load: PageServerLoad = async ({ fetch }) => {
  // 1. ユーザー取得(1秒)
  const user = await fetch('/api/user').then(r => r.json());
  // 2. 投稿取得(1秒)- ユーザー取得を待つ
  const posts = await fetch(`/api/users/${user.id}/posts`).then(r => r.json());
  // 3. コメント取得(1秒)- 投稿取得を待つ
  const comments = await fetch(`/api/users/${user.id}/comments`).then(r => r.json());
  
  // 合計: 3秒かかる
  return { user, posts, comments };
};

// ✅ 良い例:並列実行の最大化
export const load: PageServerLoad = async ({ fetch }) => {
  // 1. ユーザー取得を開始(Promiseを保持)
  const userPromise = fetch('/api/user').then(r => r.json());
  
  // 2. ユーザー情報を取得(ここで初めてawait)
  const user = await userPromise;
  
  // 3. 依存するデータを並列取得
  const [posts, comments] = await Promise.all([
    fetch(`/api/users/${user.id}/posts`).then(r => r.json()),
    fetch(`/api/users/${user.id}/comments`).then(r => r.json())
  ]);
  
  // 合計: 2秒(ユーザー取得1秒 + 並列取得1秒)
  return { user, posts, comments };
};
typescript

データの無効化と再取得

SvelteKitでは、invalidate()depends()を使用してLoad関数を選択的に再実行できます。これにより、ページ全体をリロードすることなく、必要なデータのみを効率的に更新できます。

詳細な解説

データ無効化の詳細な仕組み、depends()invalidate()の使い方、SPAモードでの動作については、 SPAモードとデータ無効化 の専用ページで包括的に解説しています。

ストリーミングSSR

ストリーミングSSRを使用すると、重要なコンテンツを即座に表示しながら、時間のかかるデータを後から段階的に送信できます。

詳細な実装方法

ストリーミングSSRの仕組み、実装パターン、最適化手法、エラーハンドリングについては、 ストリーミングSSR の専用ページで包括的に解説しています。

パフォーマンス最適化のベストプラクティス

1. 並列実行を最大化

// レイアウトとページで独立したデータは並列取得
// +layout.server.ts
export const load = async () => {
  return { user: await getUser() };
};

// +page.server.ts(レイアウトのデータを待たない)
export const load = async () => {
  return { posts: await getPosts() };
};
typescript

2. 適切なLoad関数の選択

Load関数使用場面
+page.server.ts機密データ、DBアクセス、APIキー使用
+page.ts公開API、クライアントサイド処理
+layout.server.ts共通の認証情報、セッション
+layout.ts共通のUI状態、設定

3. キャッシュ戦略

// HTTPキャッシュヘッダーの設定
export const load: PageServerLoad = async ({ setHeaders }) => {
  setHeaders({
    'cache-control': 'max-age=3600' // 1時間キャッシュ
  });
  
  return { data: await fetchData() };
};
typescript

CSR/SPAモードでのデータフロー

CSR/SPAモードでのデータフローとデータ無効化の詳細については、 SPAモードとデータ無効化 の専用ページで解説しています。

まとめ

SvelteKitのデータフローは以下の特徴を持ちます。

  • 並列実行: Load関数は可能な限り並列実行される
  • 型安全: TypeScriptによる完全な型推論
  • 柔軟な無効化: 細かい粒度でのデータ更新制御
  • ストリーミング対応: 大量データの段階的配信
  • レンダリングモードの選択: SSRとCSR/SPAを柔軟に切り替え可能

これらの仕組みを理解することで、高速でユーザー体験の良いアプリケーションを構築できます。

次のステップ

Last update at: 2025/09/16 01:10:33