# 帳票設計書 8-SSR HTML文字列

## 概要

本ドキュメントは、Vue.jsのserver-rendererパッケージにおける`renderToString`関数が出力するSSR HTML文字列（`Promise<string>`）の設計仕様を記述する。VueアプリケーションまたはVNodeをサーバーサイドでレンダリングし、完全なHTML文字列を生成する。

### 本帳票の処理概要

server-rendererの`renderToString`関数は、Vue AppインスタンスまたはVNodeを入力として受け取り、サーバーサイドでコンポーネントツリー全体をレンダリングし、非同期コンポーネントやSuspenseも含めて全てを解決した後、完全なHTML文字列をPromise<string>として返す。

**業務上の目的・背景**：SSR（サーバーサイドレンダリング）は、初回ページ表示のパフォーマンス改善とSEO対策のために必要な技術である。renderToStringは最もシンプルなSSR APIであり、完全なHTML文字列をサーバーサイドで生成してクライアントに送信する。

**帳票の利用シーン**：Node.jsサーバーまたはEdge Runtimeで、VueアプリケーションのSSRレンダリングを行う場面。小〜中規模のページや、ストリーミングが不要なケースで使用される。

**主要な出力内容**：
1. 完全なHTML文字列（Promise<string>）- コンポーネントツリー全体のレンダリング結果
2. SSRContext内のteleports - Teleportコンポーネントのレンダリング結果

**帳票の出力タイミング**：サーバーサイドでHTTPリクエストを受信し、VueアプリケーションのSSRレンダリングが必要な場面で実行される。

**帳票の利用者**：SSRフレームワーク（Nuxt.js等）、カスタムSSRサーバー、テスト環境（@vue/test-utils等）。

## 帳票種別

レンダリング結果（HTML文字列出力）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | SSRサーバー | 全てのルート | HTTPリクエスト受信時に自動実行 |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | HTML文字列（Promise<string>） |
| 用紙サイズ | 該当なし |
| 向き | 該当なし |
| ファイル名 | インメモリ（HTTPレスポンスとして送信） |
| 出力方法 | 非同期関数戻り値（Promise） |
| 文字コード | UTF-8 |

### PDF固有設定

該当なし

### Excel固有設定

該当なし

## 帳票レイアウト

### レイアウト概要

出力はHTML文字列とSSRContextの副作用の2つの要素で構成される。

```
┌─────────────────────────────────────┐
│  Promise<string>                    │
│  （完全なHTML文字列）                 │
│  例: <div id="app">...</div>       │
├─────────────────────────────────────┤
│  SSRContext (副作用)                 │
│  ├─ teleports: Record<string, string>│
│  ├─ __teleportBuffers (内部)        │
│  └─ __watcherHandles (内部)         │
└─────────────────────────────────────┘
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | result | 完全なHTML文字列 | unrollBuffer(buffer) | string |

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | teleports | Teleportコンポーネントの出力結果 | context.teleports | Record<string, string> | - |

### フッター部

該当なし

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| input | Vue AppインスタンスまたはVNode | Yes |
| context | SSRContext（Teleport等の結果を受け取るオブジェクト） | No（デフォルト: {}） |

### ソート順

該当なし

### 改ページ条件

該当なし

## データベース参照仕様

該当なし（インメモリレンダリング）

### 参照テーブル一覧

該当なし

### テーブル別参照項目詳細

該当なし

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| vnode | createVNode(input._component, input._props) | - | Appインスタンスからルートコンポーネントのvnodeを生成 |
| buffer | await renderComponentVNode(vnode) | - | コンポーネントツリーをSSRBufferにレンダリング |
| result | await unrollBuffer(buffer) | - | SSRBufferを文字列に展開（非同期Promiseを解決） |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[renderToString呼び出し] --> B{入力型判定}
    B -->|VNode| C[createAppでラップして再帰呼び出し]
    B -->|App| D[createVNodeでルートvnode生成]
    D --> E[appContext設定]
    E --> F[ssrContextKeyでcontext提供]
    F --> G[renderComponentVNode - コンポーネントレンダリング]
    G --> H[unrollBuffer - バッファ展開]
    H --> I[resolveTeleports - Teleport解決]
    I --> J{watcherHandles?}
    J -->|あり| K[全watcherをunwatch]
    J -->|なし| L[スキップ]
    K --> M[HTML文字列を返却]
    L --> M
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| レンダリングエラー | コンポーネント内でエラー発生 | コンポーネント固有のエラー | コンポーネントのエラーを修正 |
| 非同期エラー | 非同期コンポーネントの解決失敗 | Promiseのrejectメッセージ | 非同期コンポーネントのエラーハンドリング |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 1リクエストにつき1回 |
| 目標出力時間 | コンポーネントツリーのサイズに依存（数十ms〜数百ms） |
| 同時出力数上限 | サーバーリソースに依存 |

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

renderToStringはサーバーサイドで実行されるため、コンポーネント内で参照するデータがクライアントに漏洩しないよう注意が必要。SSRContextに格納されたデータはレスポンスに含まれる可能性がある。v-htmlディレクティブのHTML文字列はサニタイズされないため、XSSリスクに注意。

## 備考

- VNodeが直接渡された場合は、自動的に`createApp`でラップして再帰呼び出しされる。これによりSSRContextの提供が保証される。
- `unrollBuffer`は再帰的にSSRBufferを展開し、非同期Promiseを解決する。同期バッファの場合はより効率的な`unrollBufferSync`が使用される。
- Teleportコンポーネントの結果は`resolveTeleports`で処理され、`context.teleports`に格納される。
- `context.__watcherHandles`に登録されたウォッチャーはレンダリング完了後にunwatchされる。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | render.ts | `packages/server-renderer/src/render.ts` | SSRBuffer型（42行目）、SSRBufferItem型（43行目）、SSRContext型（47-58行目） |
| 1-2 | render.ts | `packages/server-renderer/src/render.ts` | createBuffer関数（66行目〜）- SSRBufferの生成と管理 |

**読解のコツ**: SSRBufferは再帰的な配列構造（文字列、子バッファ、Promise<SSRBuffer>の混合）であり、unrollBuffer関数で最終的にstring に展開される。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | renderToString.ts | `packages/server-renderer/src/renderToString.ts` | renderToString関数（70-97行目）がエントリーポイント |

**主要処理フロー**:
1. **74-77行目**: VNodeが渡された場合、createAppでラップして再帰呼び出し
2. **80行目**: createVNodeでルートvnodeを生成
3. **81行目**: appContextを設定
4. **83行目**: ssrContextKeyでSSRContextを提供
5. **84行目**: renderComponentVNode(vnode)でSSRBufferを生成
6. **86行目**: unrollBuffer(buffer)でbufferを文字列に展開
7. **88行目**: resolveTeleportsでTeleportを処理
8. **90-93行目**: watcherHandlesのunwatch
9. **96行目**: result（HTML文字列）を返却

#### Step 3: バッファ展開処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | renderToString.ts | `packages/server-renderer/src/renderToString.ts` | unrollBuffer関数（52-54行目）とnestedUnrollBuffer関数（14-50行目） |
| 3-2 | renderToString.ts | `packages/server-renderer/src/renderToString.ts` | unrollBufferSync関数（56-68行目）- 同期バッファの効率的展開 |

#### Step 4: Teleport解決を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | renderToString.ts | `packages/server-renderer/src/renderToString.ts` | resolveTeleports関数（99-110行目） |

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

```
renderToString (renderToString.ts:70)
    |
    +-- [VNode入力時] createApp({render: () => input}) -> 再帰呼び出し
    |
    +-- createVNode(input._component, input._props)
    |
    +-- input.provide(ssrContextKey, context)
    |
    +-- renderComponentVNode (render.ts)
    |       +-- createComponentInstance
    |       +-- setupComponent
    |       +-- renderComponentRoot
    |       +-- createBuffer / push
    |
    +-- unrollBuffer (renderToString.ts:52)
    |       +-- nestedUnrollBuffer (renderToString.ts:14) [再帰]
    |       +-- unrollBufferSync (renderToString.ts:56) [同期最適化]
    |
    +-- resolveTeleports (renderToString.ts:99)
    |
    +-- context.__watcherHandles?.forEach(unwatch)
    |
    +-- return result (string)
```

### データフロー図

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

App / VNode          ──▶  createVNode              ──▶  VNode
                                |
SSRContext                renderComponentVNode      ──▶  SSRBuffer
                                |                        (string | SSRBuffer | Promise)
                           unrollBuffer             ──▶  string (HTML)
                                |
                           resolveTeleports         ──▶  context.teleports
                                |
                           ────────────────────────────▶  Promise<string>
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| renderToString.ts | `packages/server-renderer/src/renderToString.ts` | ソース | renderToString関数、unrollBuffer、resolveTeleports |
| render.ts | `packages/server-renderer/src/render.ts` | ソース | renderComponentVNode、SSRBuffer/SSRContext型、createBuffer |
| renderToStream.ts | `packages/server-renderer/src/renderToStream.ts` | ソース | renderToSimpleStream（ストリーミング版の基盤） |
