# 機能設計書 59-renderToString()

## 概要

本ドキュメントは、@vue/server-rendererパッケージにおけるrenderToString()関数の処理について記述する。VueアプリケーションまたはVNodeをHTML文字列に変換するPromiseベースの非同期レンダリング関数である。

### 本機能の処理概要

renderToString()は、VueのAppインスタンスまたはVNodeを受け取り、コンポーネントツリー全体をサーバーサイドでレンダリングしてHTML文字列として返却する。非同期コンポーネント、Suspense、Teleportにも対応し、SSRコンテキストを通じたデータ共有もサポートする。

**業務上の目的・背景**：サーバーサイドレンダリング（SSR）において、Vueアプリケーションの初期HTMLをサーバー側で生成し、クライアントに送信する必要がある。renderToString()はこの処理の最も基本的なAPIであり、完全なHTML文字列を非同期で生成する。SEO対策やFirst Contentful Paintの高速化に貢献する。

**機能の利用シーン**：Node.jsサーバー（Express、Fastify等）やサーバーレス環境（Cloudflare Workers等）でVueアプリケーションのSSRを行う際に使用される。Nuxt.jsのSSRパイプラインでも内部的に使用される。

**主要な処理内容**：
1. VNode入力の場合はAppインスタンスでラップ
2. コンポーネントVNodeの作成とアプリケーションコンテキストの設定
3. SSRコンテキスト（ssrContextKey）のProvide
4. renderComponentVNode()によるコンポーネントツリーのレンダリング
5. SSRバッファの展開（unrollBuffer）によるHTML文字列の構築
6. Teleportバッファの解決（resolveTeleports）
7. ウォッチャーハンドルのクリーンアップ

**関連システム・外部連携**：@vue/compiler-ssrにより生成されたSSRレンダリング関数を実行する。VueのランタイムAPI（createApp、createVNode等）と連携する。

**権限による制御**：該当なし（サーバー実行時処理）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | サーバーサイドで実行されるため画面との直接的な関連はない |

## 機能種別

サーバーサイドレンダリング処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| input | App \| VNode | Yes | レンダリング対象のAppインスタンスまたはVNode | isVNode判定で自動分岐 |
| context | SSRContext | No | SSRコンテキスト（テレポート、ウォッチャー等の状態共有） | デフォルト: {} |

### 入力データソース

createApp()で生成されたVueアプリケーションインスタンス、またはcreateVNode()/h()で生成されたVNode。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result | Promise<string> | レンダリングされたHTML文字列のPromise |

### 出力先

HTML文字列として返却される。通常はHTTPレスポンスとしてクライアントに送信される。

## 処理フロー

### 処理シーケンス

```
1. 入力判定
   └─ VNodeの場合: createApp({ render: () => input })でAppにラップ
   └─ Appの場合: そのまま使用

2. コンポーネントVNodeの生成
   └─ createVNode(input._component, input._props)
   └─ vnode.appContext = input._context

3. SSRコンテキストの注入
   └─ input.provide(ssrContextKey, context)

4. コンポーネントツリーのレンダリング
   └─ renderComponentVNode(vnode)
   └─ 結果はSSRBuffer（文字列、ネストされたバッファ、Promiseの配列）

5. バッファの展開
   └─ unrollBuffer(buffer)
   └─ 再帰的にSSRBufferをフラットなHTML文字列に変換
   └─ 非同期バッファ（Promise）はawaitで解決

6. Teleportの解決
   └─ resolveTeleports(context)
   └─ __teleportBuffersの各バッファを展開してcontext.teleportsに格納

7. ウォッチャーのクリーンアップ
   └─ context.__watcherHandlesの各unwatchを呼び出し

8. 結果の返却
   └─ HTML文字列を返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始: renderToString] --> B{VNode入力?}
    B -->|Yes| C[createAppでラップ]
    B -->|No| D[Appとして使用]
    C --> E[再帰呼び出し]
    D --> F[createVNode]
    F --> G[SSRコンテキスト注入]
    G --> H[renderComponentVNode]
    H --> I[awaitバッファ]
    I --> J[unrollBuffer]
    J --> K[resolveTeleports]
    K --> L{ウォッチャーあり?}
    L -->|Yes| M[unwatchAll]
    L -->|No| N[HTML文字列返却]
    M --> N
    N --> O[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-59-01 | VNode自動ラップ | VNode入力はcreateApp()で自動的にAppインスタンスにラップ | isVNode(input) === true |
| BR-59-02 | Teleport解決 | Teleportのコンテンツは別途context.teleportsに格納 | __teleportBuffersが存在する場合 |
| BR-59-03 | ウォッチャークリーンアップ | SSR後にウォッチャーハンドルをすべて解除 | __watcherHandlesが存在する場合 |
| BR-59-04 | 同期バッファ最適化 | hasAsync=falseのバッファは同期的に展開 | buffer.hasAsync === false |

### 計算ロジック

SSRバッファ展開:
- バッファは文字列、サブバッファ（再帰配列）、Promise（非同期サブバッファ）の混合配列
- hasAsyncフラグにより、同期バッファの高速パスを実現
- nestedUnrollBufferで再帰的に展開し、Promiseを解決

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

該当なし（サーバーサイドレンダリング処理）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | Error | コンポーネントのsetup()でエラー | コンポーネントのエラーハンドリングを実装する |
| - | Error | 非同期コンポーネントの読み込み失敗 | defineAsyncComponent()のエラーハンドラを設定する |
| - | Warning | テンプレート/レンダリング関数が存在しない | テンプレートまたはrender関数を定義する |

### リトライ仕様

該当なし（エラーはPromiseのrejectとして伝播）

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

該当なし

## パフォーマンス要件

- hasAsyncフラグによる同期バッファの高速展開（不要なawaitティックを回避）
- appendable最適化（連続する文字列アイテムの結合）
- SSRコンパイル済みコンポーネントのssrRenderによる直接的な文字列出力（VNode生成を回避）

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

- テキストコンテンツはescapeHtml()でHTMLエスケープされる（XSS対策）
- コメントはescapeHtmlComment()でエスケープされる
- Teleportコンテンツもエスケープ処理が適用される

## 備考

- renderToString()は全コンテンツをメモリに保持するため、大きなページではストリーミングAPI（renderToNodeStream、renderToWebStream）の使用が推奨される
- SSRコンテキスト（SSRContext）はコンポーネントツリー全体で共有され、Teleportバッファやウォッチャーハンドルを管理する
- onServerPrefetch()フックは、renderComponentVNode内でのsetupComponentの後に実行される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | render.ts | `packages/server-renderer/src/render.ts` | SSRBuffer、SSRBufferItem、PushFn、SSRContext型定義 |
| 1-2 | render.ts | `packages/server-renderer/src/render.ts` | createBuffer()関数 - バッファの構造 |

**読解のコツ**: SSRBufferは配列型で、要素は文字列（直接HTMLテキスト）、サブバッファ（ネストされたSSRBuffer）、Promise（非同期レンダリング結果）の3種類。hasAsyncフラグで非同期要素の有無を追跡する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | renderToString.ts | `packages/server-renderer/src/renderToString.ts` | renderToString()関数 |

**主要処理フロー**:
1. **70-77行目**: 入力判定。VNodeの場合はcreateAppでラップして再帰呼び出し
2. **80-81行目**: createVNodeでコンポーネントVNodeを生成し、appContextを設定
3. **83行目**: SSRコンテキストの注入（provide）
4. **84行目**: renderComponentVNode()でコンポーネントツリーをレンダリング
5. **86行目**: unrollBuffer()でSSRバッファをHTML文字列に展開
6. **88行目**: resolveTeleports()でTeleportバッファを解決
7. **90-93行目**: ウォッチャーハンドルのクリーンアップ
8. **95行目**: HTML文字列を返却

#### Step 3: バッファ展開の詳細

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | renderToString.ts | `packages/server-renderer/src/renderToString.ts` | unrollBuffer/nestedUnrollBuffer関数 |

**主要処理フロー**:
- **14-50行目**: nestedUnrollBuffer - 再帰的なバッファ展開。hasAsync=falseの場合は同期展開（unrollBufferSync）
- **52-54行目**: unrollBuffer - エントリーポイント
- **56-68行目**: unrollBufferSync - 同期バッファの高速展開

#### Step 4: コンポーネントレンダリングの詳細

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | render.ts | `packages/server-renderer/src/render.ts` | renderComponentVNode関数 |

**主要処理フロー**:
- **91-123行目**: renderComponentVNode - コンポーネントインスタンス生成、setupComponent実行、非同期setup/serverPrefetchの処理
- **125-221行目**: renderComponentSubTree - ssrRender関数があれば直接実行、なければVNodeツリーのレンダリング
- **223-279行目**: renderVNode - VNodeの種別（テキスト、コメント、要素、コンポーネント等）に応じたレンダリング

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

```
renderToString (エントリーポイント)
    │
    ├─ createApp / createVNode
    │
    ├─ renderComponentVNode
    │      ├─ createComponentInstance
    │      ├─ setupComponent (SSR)
    │      ├─ ServerPrefetch処理
    │      └─ renderComponentSubTree
    │             ├─ ssrRender (コンパイル済み)
    │             │      └─ _push(テンプレートリテラル)
    │             ├─ renderComponentRoot (非コンパイル済み)
    │             │      └─ renderVNode
    │             └─ ssrCompile (JIT)
    │
    ├─ unrollBuffer
    │      ├─ nestedUnrollBuffer (非同期)
    │      └─ unrollBufferSync (同期)
    │
    ├─ resolveTeleports
    │      └─ unrollBuffer (各Teleportバッファ)
    │
    └─ ウォッチャークリーンアップ
```

### データフロー図

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

App / VNode        ───▶ renderToString           ───▶ Promise<string>
                         │                            (HTML文字列)
                         ├─ createVNode
                         │   → コンポーネントVNode     SSRContext.teleports
                         │                            (Teleportバッファ)
                         ├─ renderComponentVNode
                         │   → SSRBuffer
                         │   (文字列 + Promise)
                         │
                         ├─ unrollBuffer
                         │   SSRBuffer → string
                         │
                         └─ resolveTeleports
                             __teleportBuffers
                             → context.teleports
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| renderToString.ts | `packages/server-renderer/src/renderToString.ts` | ソース | renderToString()のメイン実装 |
| render.ts | `packages/server-renderer/src/render.ts` | ソース | SSRバッファ、renderComponentVNode、renderVNode |
| renderToStream.ts | `packages/server-renderer/src/renderToStream.ts` | ソース | ストリーミングレンダリング（関連API） |
| index.ts | `packages/server-renderer/src/index.ts` | ソース | パッケージのエクスポート定義 |
| ssrCompile.ts | `packages/server-renderer/src/helpers/ssrCompile.ts` | ソース | JITコンパイル（ssrRenderが無い場合） |
| ssrRenderAttrs.ts | `packages/server-renderer/src/helpers/ssrRenderAttrs.ts` | ソース | 属性のSSRレンダリング |
| ssrRenderList.ts | `packages/server-renderer/src/helpers/ssrRenderList.ts` | ソース | リストのSSRレンダリング |
| ssrRenderSlot.ts | `packages/server-renderer/src/helpers/ssrRenderSlot.ts` | ソース | スロットのSSRレンダリング |
| ssrInterpolate.ts | `packages/server-renderer/src/helpers/ssrInterpolate.ts` | ソース | 補間のSSRレンダリング |
| ssrRenderTeleport.ts | `packages/server-renderer/src/helpers/ssrRenderTeleport.ts` | ソース | TeleportのSSRレンダリング |
| ssrRenderSuspense.ts | `packages/server-renderer/src/helpers/ssrRenderSuspense.ts` | ソース | SuspenseのSSRレンダリング |
| ssrRenderComponent.ts | `packages/server-renderer/src/helpers/ssrRenderComponent.ts` | ソース | コンポーネントのSSRレンダリング |
