# 機能設計書 53-静的コンテンツキャッシング

## 概要

本ドキュメントは、Vue 3テンプレートコンパイラにおける静的コンテンツキャッシング（hoistStatic）の処理について記述する。静的ノードのホイスティングとキャッシングにより、再レンダリング時の不要なVNode生成を回避し、パフォーマンスを最適化する。

### 本機能の処理概要

静的コンテンツキャッシングは、テンプレートAST内の静的ノード（変更されないコンテンツ）を検出し、レンダリング関数の外側にホイストしてキャッシュする最適化処理である。これにより、コンポーネントの再レンダリング時に同じ静的VNodeを再生成する必要がなくなり、メモリ使用量とCPU処理の削減が実現される。

**業務上の目的・背景**：Vueの仮想DOM差分比較アルゴリズムにおいて、静的コンテンツが変更されないことが事前に判明していれば、差分比較自体をスキップできる。コンパイル時に静的性を分析してフラグを設定することで、ランタイムのパフォーマンスを大幅に向上させる。特に大量の静的HTMLを含むテンプレートで効果が顕著である。

**機能の利用シーン**：テンプレートコンパイル時にhoistStaticオプションが有効な場合に自動的に実行される。compileTemplate()のデフォルト設定で有効であり、SFCコンパイルの標準パイプラインに組み込まれている。

**主要な処理内容**：
1. AST全体の再帰的走査と各ノードの定数タイプ（ConstantType）判定
2. 静的ノードの検出とCACHEDフラグの付与
3. 静的プロパティのホイスティング（ノード全体が静的でない場合もpropsをホイスト）
4. 子配列全体のキャッシュ最適化
5. スロット内の静的コンテンツのキャッシュ
6. transformHoistによるカスタムホイスト変換のサポート

**関連システム・外部連携**：@vue/compiler-coreパッケージのtransform関数から呼び出される。コード生成（Codegen）と連携し、ホイストされた変数と_cache配列のコードを出力する。

**権限による制御**：該当なし（コンパイル時処理）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 4 | Index（メイン画面） | 補助機能 | hoistStatic有効時の静的ノードホイスティング最適化結果の確認 |

## 機能種別

コンパイル時最適化処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| root | RootNode | Yes | 変換済みのテンプレートAST | transformフェーズ完了後 |
| context | TransformContext | Yes | 変換コンテキスト（hoists/cached配列を保持） | - |

### 入力データソース

transform関数によるAST変換が完了した後のRootNode。各ノードにはcodegenNodeが設定されている。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| context.hoists | (JSChildNode \| null)[] | ホイストされた静的プロパティの配列 |
| context.cached | (CacheExpression \| null)[] | キャッシュされた静的ノードの配列 |
| node.codegenNode.patchFlag | PatchFlags.CACHED | 静的ノードに付与されるキャッシュフラグ |

### 出力先

変換後のRootNodeおよびTransformContextのhoists/cached配列。コード生成（Codegen）で`_hoisted_N`変数と`_cache[N]`アクセスとして出力される。

## 処理フロー

### 処理シーケンス

```
1. エントリーポイント: cacheStatic
   └─ getSingleElementRootの判定（ルートノードは属性フォールスルーのためホイスト不可）
   └─ walk関数を開始

2. 子ノードの走査（walk関数）
   └─ 各子ノードの定数タイプを判定（getConstantType）
   └─ ELEMENTノード:
      - CAN_CACHE以上: patchFlagにCACHEDを設定、toCacheに追加
      - NOT_CONSTANT: propsの定数タイプをチェックし、ホイスト可能ならpropsをホイスト
      - dynamicPropsもホイスト対象
   └─ TEXT_CALLノード:
      - CAN_CACHE以上: 引数にCACHEDフラグを追加、toCacheに追加

3. 子ノードの再帰走査
   └─ ELEMENTノード: コンポーネントの場合はvSlotスコープをインクリメント
   └─ FORノード: 単一子はブロックのためホイスト不可
   └─ IFノード: 各ブランチを再帰走査

4. 配列全体のキャッシュ最適化
   └─ toCache.length === children.length の場合:
      - 要素のchildren配列全体をキャッシュ
      - コンポーネントのデフォルトスロット内容をキャッシュ
      - 名前付きtemplateスロット内容をキャッシュ
   └─ それ以外: 各ノードを個別にキャッシュ

5. transformHoistの呼び出し
   └─ カスタムホイスト変換があれば実行（SSR stringification等）
```

### フローチャート

```mermaid
flowchart TD
    A[開始: cacheStatic] --> B{単一要素ルート?}
    B -->|Yes| C[doNotHoistNode = true]
    B -->|No| D[doNotHoistNode = false]
    C --> E[walk開始]
    D --> E
    E --> F[子ノードを反復]
    F --> G{ノード種別?}
    G -->|ELEMENT| H[getConstantType判定]
    G -->|TEXT_CALL| I[getConstantType判定]
    G -->|その他| J[再帰走査]
    H --> K{定数タイプ >= CAN_CACHE?}
    K -->|Yes| L[patchFlag = CACHED, toCache追加]
    K -->|No| M{propsがCAN_CACHE?}
    M -->|Yes| N[propsをホイスト]
    M -->|No| J
    I --> O{定数タイプ >= CAN_CACHE?}
    O -->|Yes| P[CACHED引数追加, toCache追加]
    O -->|No| J
    L --> J
    N --> J
    P --> J
    J --> Q{全子がキャッシュ対象?}
    Q -->|Yes| R[配列全体をキャッシュ]
    Q -->|No| S[個別にキャッシュ]
    R --> T[transformHoist呼び出し]
    S --> T
    T --> U[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-53-01 | ルートホイスト不可 | 単一要素ルートは属性フォールスルーのためホイスト不可 | getSingleElementRoot(root)がnullでない場合 |
| BR-53-02 | ブロックホイスト不可 | isBlock=trueのノードはホイスト不可（svg/foreignObject/math除く） | codegenNode.isBlock && tag !== 'svg/foreignObject/math' |
| BR-53-03 | v-for単一子不可 | v-forの単一子はブロックであるためホイスト不可 | child.children.length === 1のFORノード |
| BR-53-04 | v-if単一子不可 | v-ifの単一子はブロックであるためホイスト不可 | branch.children.length === 1のIFブランチ |
| BR-53-05 | 定数キャッシュ | WeakMapベースの定数キャッシュで重複判定を回避 | constantCache.get(node)で既存結果を再利用 |
| BR-53-06 | 配列スプレッド | キャッシュされた配列はメモリリーク防止のためスプレッドを使用 | needArraySpread = trueが設定される |

### 計算ロジック

ConstantTypes判定の階層:
- `NOT_CONSTANT` (0): 動的ノード（ホイスト不可）
- `CAN_SKIP_PATCH` (1): パッチをスキップ可能（ランタイム最適化のみ）
- `CAN_CACHE` (2): キャッシュ可能（ホイスト対象）
- `CAN_STRINGIFY` (3): 文字列化可能（最高レベルの静的性）

getConstantType判定ルール:
1. コンポーネント/スロットノード: NOT_CONSTANT
2. patchFlagが設定されている要素: NOT_CONSTANT
3. patchFlagが未設定の要素: props/children/v-bind式をすべて再帰チェック
4. TEXT/COMMENT: CAN_STRINGIFY
5. IF/FOR/IF_BRANCH: NOT_CONSTANT
6. INTERPOLATION/TEXT_CALL: 子のconstTypeに依存
7. SIMPLE_EXPRESSION: node.constType
8. COMPOUND_EXPRESSION: 最小のchildType

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

該当なし（コンパイル時処理のため、データベース操作は発生しない）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | - | 本処理ではエラーは発生しない（最適化のみ） | - |

### リトライ仕様

該当なし

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

該当なし（コンパイル時処理）

## パフォーマンス要件

- WeakMapベースのconstantCacheにより、同一ノードの重複判定を回避
- 配列全体のキャッシュにより、個別キャッシュよりも効率的な最適化を実現
- needArraySpreadによるメモリリーク防止（キャッシュされた配列のDOM参照が保持され続ける問題を回避）

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

該当なし（コンパイル時処理であり、ユーザー入力の直接処理は行わない）

## 備考

- SSRモードではhoistStaticは無効化される（SSRは文字列連結のため、ホイスティングの効果がない）
- transformHoistはstringify最適化（静的ノードを単一のinnerHTML文字列に変換）に使用される
- キャッシュされた配列のメモリリーク問題（#6978, #7138, #7114, #13221）への対応としてneedArraySpreadフラグが導入された

---

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

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

### 推奨読解順序

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

まず、定数タイプとキャッシュ式の型定義を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ast.ts | `packages/compiler-core/src/ast.ts` | ConstantTypes列挙型（NOT_CONSTANT/CAN_SKIP_PATCH/CAN_CACHE/CAN_STRINGIFY） |
| 1-2 | ast.ts | `packages/compiler-core/src/ast.ts` | CacheExpression型（index, value, isVNode, needArraySpread） |
| 1-3 | transform.ts | `packages/compiler-core/src/transform.ts` | TransformContextのhoist()とcache()メソッド |

**読解のコツ**: hoistは式をモジュールスコープ変数（`_hoisted_N`）に移動する処理、cacheはレンダリング関数内の`_cache[N]`に保存する処理である。両者は異なるスコープレベルの最適化を担う。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | transform.ts | `packages/compiler-core/src/transform.ts` | transform関数内でのcacheStatic呼び出し |

**主要処理フロー**:
1. **335-337行目**: `options.hoistStatic`が有効な場合にcacheStatic(root, context)を呼び出し

#### Step 3: cacheStatic関数の詳細

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | cacheStatic.ts | `packages/compiler-core/src/transforms/cacheStatic.ts` | cacheStatic関数 |

**主要処理フロー**:
- **40-49行目**: cacheStatic - エントリーポイント。getSingleElementRootで単一要素ルート判定
- **51-60行目**: getSingleElementRoot - コメントを除く子が1つのELEMENTかどうか判定

#### Step 4: walk関数の詳細

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | cacheStatic.ts | `packages/compiler-core/src/transforms/cacheStatic.ts` | walk関数 |

**主要処理フロー**:
- **62-68行目**: walk関数のシグネチャとパラメータ
- **71-127行目**: 子ノードの走査ループ。ELEMENT/TEXT_CALLの定数タイプ判定とキャッシュ/ホイスト決定
- **130-153行目**: 再帰走査（コンポーネント、v-for、v-if）
- **156-215行目**: 配列全体のキャッシュ最適化（要素、コンポーネントデフォルトスロット、名前付きスロット）
- **211-215行目**: 個別キャッシュ（配列全体のキャッシュが適用されない場合）
- **248-251行目**: transformHoist呼び出し

#### Step 5: getConstantType関数の詳細

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | cacheStatic.ts | `packages/compiler-core/src/transforms/cacheStatic.ts` | getConstantType関数 |

**主要処理フロー**:
- **253-392行目**: ノード種別ごとの定数タイプ判定
- **259-355行目**: ELEMENTノード - patchFlag未設定時のprops/children/v-bind再帰チェック
- **356-358行目**: TEXT/COMMENT → CAN_STRINGIFY
- **359-362行目**: IF/FOR/IF_BRANCH → NOT_CONSTANT

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

```
transform (エントリーポイント)
    │
    └─ cacheStatic
           │
           ├─ getSingleElementRoot
           │
           └─ walk (再帰)
                  │
                  ├─ getConstantType (再帰)
                  │      ├─ getGeneratedPropsConstantType
                  │      │      └─ getNodeProps
                  │      └─ getConstantTypeOfHelperCall
                  │
                  ├─ context.hoist(props) - 静的propsのホイスト
                  │
                  ├─ context.cache(codegenNode) - ノードのキャッシュ
                  │
                  ├─ getCacheExpression - 配列キャッシュ
                  │
                  ├─ getSlotNode - スロット内容取得
                  │
                  └─ context.transformHoist - カスタムホイスト
```

### データフロー図

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

RootNode           ───▶ cacheStatic             ───▶ context.hoists[]
(変換済みAST)             │                            (_hoisted_1, _hoisted_2, ...)
                         ├─ walk
                         │   ├─ getConstantType        context.cached[]
                         │   │   → CAN_CACHE判定       (_cache[0], _cache[1], ...)
                         │   │
                         │   ├─ patchFlag = CACHED     node.codegenNode
                         │   │                         (patchFlag更新)
                         │   └─ props hoist
                         │       → _hoisted_N
                         │
                         └─ transformHoist
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| cacheStatic.ts | `packages/compiler-core/src/transforms/cacheStatic.ts` | ソース | 静的コンテンツキャッシングのメイン実装 |
| transform.ts | `packages/compiler-core/src/transform.ts` | ソース | cacheStaticの呼び出し元、TransformContextのhoist/cacheメソッド |
| ast.ts | `packages/compiler-core/src/ast.ts` | ソース | ConstantTypes、CacheExpression型定義 |
| codegen.ts | `packages/compiler-core/src/codegen.ts` | ソース | ホイスト変数とキャッシュアクセスのコード生成 |
| runtimeHelpers.ts | `packages/compiler-core/src/runtimeHelpers.ts` | ソース | OPEN_BLOCK等のヘルパーシンボル（ホイスト時に除去） |
| utils.ts | `packages/compiler-core/src/utils.ts` | ソース | isSlotOutlet等のユーティリティ関数 |
