# 機能設計書 23-onServerPrefetch()

## 概要

本ドキュメントは、Vue 3の`onServerPrefetch()`ライフサイクルフックの機能設計を記述する。SSR（サーバーサイドレンダリング）時にコンポーネントのレンダリング前にデータプリフェッチを行うための専用フックである。

### 本機能の処理概要

`onServerPrefetch()`は、サーバーサイドレンダリング時にのみ実行されるライフサイクルフックであり、コンポーネントのレンダリング前に非同期データの取得を完了させるために使用される。クライアントサイドでは登録自体がスキップされる。

**業務上の目的・背景**：SSRアプリケーションにおいて、初期表示時にサーバー側でデータフェッチを完了させ、完全なHTMLをクライアントに返すことが求められる。これにより、SEO対策や初期表示速度の向上を実現する。`onServerPrefetch`はこのサーバー側データフェッチの標準的なインターフェースを提供する。

**機能の利用シーン**：SSRアプリケーションでのAPIデータ取得、データベースクエリの実行結果をコンポーネント状態に反映、ストアへの初期データ投入（Pinia等との連携）、SEO対策のためのメタデータ取得。

**主要な処理内容**：
1. `onServerPrefetch(hook)`でプリフェッチフックをコンポーネントインスタンスの`sp`（server prefetch）配列に登録
2. `createHook`関数内で`isInSSRComponentSetup`チェックを行うが、`SERVER_PREFETCH`の場合はSSR中でもスキップしない特別扱い
3. サーバーレンダラー（`@vue/server-renderer`）のレンダリング処理中に`instance.sp`配列のフックを`Promise.all`で並列実行
4. 全プリフェッチ完了後にコンポーネントのレンダリングを実行

**関連システム・外部連携**：`@vue/server-renderer`パッケージ（SSRレンダリング処理）、非同期データフェッチAPI（fetch、axios等）、状態管理ライブラリ（Pinia、Vuex等）と連携する。

**権限による制御**：権限による制御は存在しない。Composition APIのsetup()関数内でのみ使用可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 画面機能マッピングに直接の関連画面なし。SSR対応アプリケーションの任意のコンポーネントで使用される |

## 機能種別

ライフサイクルフック登録 / データプリフェッチ

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| hook | () => Promise\<any\> | Yes | サーバー側で実行されるプリフェッチ関数。Promiseを返すことが推奨される | なし |
| target | ComponentInternalInstance \| null | No | フックを登録する対象コンポーネントインスタンス（省略時はcurrentInstance） | なし |

### 入力データソース

Composition APIのsetup()関数内からのプログラム呼び出し。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| void | void | 戻り値なし。副作用としてコンポーネントインスタンスにフック関数を登録する |

### 出力先

コンポーネントインスタンス内部の`sp`（server prefetch hooks配列）プロパティ。

## 処理フロー

### 処理シーケンス

```
1. onServerPrefetch(hook) 呼び出し（setup()内）
   └─ createHook(LifecycleHooks.SERVER_PREFETCH) で生成された関数を実行
2. createHook内のSSRチェック
   └─ isInSSRComponentSetupチェック：SERVER_PREFETCHの場合はSSR中でもスキップしない
3. injectHook(SERVER_PREFETCH, hook, target) でフック登録
   └─ target.sp配列にラッパー関数を追加
4. サーバーレンダリング時にinstance.spを取得
   └─ Promise.all(prefetches.map(prefetch => prefetch.call(instance.proxy)))
5. 全プリフェッチ完了後
   └─ コンポーネントのレンダリング処理を実行
```

### フローチャート

```mermaid
flowchart TD
    A[onServerPrefetch呼び出し] --> B{SSR環境?}
    B -->|Yes| C[injectHookでフック登録]
    B -->|No - SSR中でない| C
    C --> D[instance.sp配列に追加]
    D --> E{サーバーレンダリング実行時}
    E --> F[instance.sp取得]
    F --> G{非同期setup完了待ち}
    G --> H[Promise.allで全prefetchを並列実行]
    H --> I[全プリフェッチ完了]
    I --> J[コンポーネントレンダリング]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-23-01 | SSR専用実行 | サーバーレンダラーのレンダリング処理中にのみフックが実行される。クライアント側では登録されてもフックは呼ばれない | SSR時 |
| BR-23-02 | SSR中もフック登録可能 | createHook内のisInSSRComponentSetupチェックにおいて、SERVER_PREFETCHは例外的にSSR中でも登録がスキップされない | フック登録時 |
| BR-23-03 | 並列実行 | instance.sp配列内の全フックはPromise.allにより並列実行される | サーバーレンダリング時 |
| BR-23-04 | 非同期setup対応 | 非同期setupの場合、setup完了後にinstance.spを再評価する | 非同期setupコンポーネント |

### 計算ロジック

該当なし。

## データベース操作仕様

### 操作別データベース影響一覧

該当なし（フレームワークのライフサイクル管理機能）。フック内でのデータベース操作はアプリケーション側の実装による。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 開発警告 | setup()関数外でonServerPrefetchを呼び出した場合 | activeなコンポーネントインスタンスが存在しない旨の警告を出力 |
| SERVER_PREFETCH | ランタイムエラー | プリフェッチ関数内で例外が発生した場合 | Promiseチェーンでエラーをキャッチ |

### リトライ仕様

フレームワーク側にリトライ機構は存在しない。アプリケーション側でプリフェッチ関数内にリトライロジックを実装可能。

## トランザクション仕様

該当なし。

## パフォーマンス要件

- プリフェッチフックはPromise.allにより並列実行されるため、複数のデータフェッチを効率的に処理可能
- プリフェッチの完了を待ってからレンダリングが開始されるため、プリフェッチの実行時間がSSRレスポンスタイムに直接影響する

## セキュリティ考慮事項

- サーバー側で実行されるため、APIキーやデータベース接続情報がクライアントに漏洩しないよう注意が必要
- プリフェッチで取得したデータはクライアントへのhydration用データとしてHTMLに含まれるため、機密情報の混入に注意

## 備考

- `onServerPrefetch`は`LifecycleHooks.SERVER_PREFETCH`（内部キー: `'sp'`）に対応
- クライアント側ではフックが登録されても実行されないため、クライアント/サーバー両方で動作するコードでも安全に使用可能
- Options APIの`serverPrefetch`オプションに対応するComposition API

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

ライフサイクルフックの種別定義を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | enums.ts | `packages/runtime-core/src/enums.ts` | `LifecycleHooks.SERVER_PREFETCH = 'sp'`。コンポーネントインスタンスの`sp`プロパティに対応 |

**読解のコツ**: `SERVER_PREFETCH`はenums.tsの15行目に定義されている。このキー`'sp'`がコンポーネントインスタンスのプロパティ名として使用される。

#### Step 2: エントリーポイントを理解する

`onServerPrefetch`の定義と、SSR中の特別扱いを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | apiLifecycle.ts | `packages/runtime-core/src/apiLifecycle.ts` | `onServerPrefetch`の定義（95-97行目）と`createHook`のSSRチェック（72-79行目） |

**主要処理フロー**:
1. **66-79行目**: `createHook`関数。`isInSSRComponentSetup`チェックにおいて、`lifecycle === LifecycleHooks.SERVER_PREFETCH`の場合はSSR中でも登録をスキップしない
2. **95-97行目**: `onServerPrefetch`定義。`createHook(LifecycleHooks.SERVER_PREFETCH)`で生成

#### Step 3: サーバーレンダラーでの実行を理解する

サーバーレンダラーがinstance.spを実行する仕組みを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | render.ts | `packages/server-renderer/src/render.ts` | SSRレンダリング中のprefetchフック実行（105-114行目） |

**主要処理フロー**:
- **105行目**: `let prefetches = instance.sp`でprefetchフック配列を取得
- **106行目**: `hasAsyncSetup || prefetches`で実行条件判定
- **107-114行目**: `Promise.resolve(res).then(() => { ... })`で非同期setup完了を待ち、`Promise.all(prefetches.map(prefetch => prefetch.call(instance.proxy)))`で全フックを並列実行
- **110行目**: 非同期setupの場合、setup解決後に`instance.sp`を再取得

### プログラム呼び出し階層図

```
onServerPrefetch(hook)
    │
    └─ createHook(SERVER_PREFETCH) で生成された関数
           │
           ├─ isInSSRComponentSetup チェック（SERVER_PREFETCHはスキップしない）
           │
           └─ injectHook(SERVER_PREFETCH, hook, target)
                  └─ target.sp[] にフック追加

[SSRレンダリング時 - server-renderer]
renderComponentVNode(vnode)
    │
    └─ instance.sp 取得
           │
           └─ Promise.all(prefetches.map(p => p.call(instance.proxy)))
                  └─ 全プリフェッチ完了後レンダリング
```

### データフロー図

```
[入力]                    [処理]                          [出力]

hook (Function)  ───▶ createHook / injectHook      ───▶ instance.sp[]
                                                          （フック配列に追加）

[SSRレンダリング時]
instance.sp[]    ───▶ Promise.all(map(call))       ───▶ 非同期データ取得完了
                                                          │
                                                          ▼
                                                   コンポーネントレンダリング
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| apiLifecycle.ts | `packages/runtime-core/src/apiLifecycle.ts` | ソース | onServerPrefetch定義、createHook関数、injectHook関数 |
| enums.ts | `packages/runtime-core/src/enums.ts` | ソース | LifecycleHooks.SERVER_PREFETCH定義 |
| render.ts | `packages/server-renderer/src/render.ts` | ソース | SSRレンダリング中のprefetchフック実行ロジック |
| errorHandling.ts | `packages/runtime-core/src/errorHandling.ts` | ソース | ErrorTypeStringsにSERVER_PREFETCHのエラー文字列定義 |
