# 機能設計書 25-provide() / inject()

## 概要

本ドキュメントは、Vue 3の`provide()`および`inject()`APIの機能設計を記述する。親コンポーネントから子孫コンポーネントへの依存性注入（Dependency Injection）メカニズムであり、プロップスドリリングを回避するための中核的なAPIである。

### 本機能の処理概要

`provide()`は親コンポーネントからデータを提供し、`inject()`は子孫コンポーネントでそのデータを取得するAPIである。JavaScriptのプロトタイプチェーンを活用して、コンポーネントツリーの深さに関係なく効率的にデータを伝達する。

**業務上の目的・背景**：大規模なVueアプリケーションにおいて、深くネストされたコンポーネントに対してプロップスを中間コンポーネント経由で逐次伝達する「プロップスドリリング」は、コードの保守性と可読性を著しく低下させる。provide/injectはこの問題を解決し、テーマ設定、ロケール情報、認証状態などのアプリケーション全体で共有すべきデータを効率的に伝達する。

**機能の利用シーン**：テーマ・ロケールの共有、認証/認可情報の伝達、フォームコンテキストの共有、プラグインによるグローバルサービスの提供、コンポーネントライブラリのコンテキスト管理。

**主要な処理内容**：
1. `provide(key, value)`で親コンポーネントのprovidesオブジェクトにキー・値ペアを設定
2. 初回provide時にプロトタイプチェーンを利用した新しいprovidesオブジェクトを生成
3. `inject(key, defaultValue?, treatDefaultAsFactory?)`で祖先のprovidesチェーンからキーに対応する値を取得
4. 値が見つからない場合はデフォルト値を返すか、開発モードで警告を出力
5. `hasInjectionContext()`でinject可能なコンテキストの存在を確認

**関連システム・外部連携**：`app.provide()`（アプリケーションレベルのprovide）、`app.runWithContext()`（コンテキスト付き実行）、Vue Router/Pinia等のプラグインと連携する。

**権限による制御**：権限による制御は存在しない。provide()はsetup()関数内でのみ使用可能。inject()はsetup()関数内または関数型コンポーネント内で使用可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 画面機能マッピングに直接の関連画面なし。アプリケーション全体で使用される依存性注入機能 |

## 機能種別

依存性注入 / データ共有

## 入力仕様

### 入力パラメータ

#### provide()

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| key | InjectionKey\<T\> \| string \| number | Yes | 注入キー。InjectionKeyはSymbolベースの型安全なキー | なし |
| value | T | Yes | 提供する値 | なし |

#### inject()

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| key | InjectionKey\<T\> \| string | Yes | 注入キー | なし |
| defaultValue | T \| (() => T) | No | 値が見つからない場合のデフォルト値 | なし |
| treatDefaultAsFactory | boolean | No | trueの場合、defaultValueをファクトリ関数として扱う | なし |

### 入力データソース

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

## 出力仕様

### 出力データ

#### provide()

| 項目名 | 型 | 説明 |
|--------|-----|------|
| void | void | 戻り値なし。副作用としてprovidesオブジェクトに値を設定 |

#### inject()

| 項目名 | 型 | 説明 |
|--------|-----|------|
| value | T \| undefined | 注入された値。見つからずデフォルト値も未指定の場合はundefined |

### 出力先

provide(): コンポーネントインスタンスの`provides`プロパティ。
inject(): 呼び出し元への戻り値。

## 処理フロー

### 処理シーケンス

```
[provide]
1. provide(key, value) 呼び出し
   └─ currentInstanceの存在確認
2. 現在のprovidesが親のprovidesと同一か確認
   └─ 同一の場合：Object.create(parentProvides) で新オブジェクト生成
3. provides[key] = value で値を設定

[inject]
1. inject(key, defaultValue, treatDefaultAsFactory) 呼び出し
   └─ getCurrentInstance() でインスタンス取得
2. providesオブジェクトの決定
   ├─ currentAppが存在する場合：currentApp._context.provides
   ├─ ルートまたはカスタム要素の場合：vnode.appContext.provides
   └─ それ以外：instance.parent.provides
3. key in provides でキーの存在確認
   ├─ 存在する場合：provides[key] を返却
   └─ 存在しない場合：
       ├─ デフォルト値あり：defaultValue（ファクトリの場合は呼び出し結果）を返却
       └─ デフォルト値なし：開発警告を出力
```

### フローチャート

```mermaid
flowchart TD
    subgraph provide
        PA[provide key, value] --> PB{currentInstance存在?}
        PB -->|Yes| PC{provides === parentProvides?}
        PB -->|No| PD[開発警告]
        PC -->|Yes| PE[Object.create parentProvides]
        PC -->|No| PF[既存providesを使用]
        PE --> PG[provides key = value]
        PF --> PG
    end

    subgraph inject
        IA[inject key] --> IB{instance or currentApp存在?}
        IB -->|Yes| IC[providesオブジェクト決定]
        IB -->|No| ID[開発警告]
        IC --> IE{key in provides?}
        IE -->|Yes| IF[provides key を返却]
        IE -->|No| IG{デフォルト値あり?}
        IG -->|Yes| IH{ファクトリ関数?}
        IG -->|No| II[開発警告 + undefined返却]
        IH -->|Yes| IJ[defaultValue.call実行]
        IH -->|No| IK[defaultValue返却]
    end
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-25-01 | プロトタイプチェーン継承 | 初回provide時にObject.create(parentProvides)で親のprovidesをプロトタイプとする新オブジェクトを生成 | 初回provide呼び出し時 |
| BR-25-02 | キーの優先順位 | ネストされたcreateApp環境ではcurrentApp._context.providesが優先される（#11488） | ネストアプリケーション時 |
| BR-25-03 | カスタム要素対応 | カスタム要素（instance.ce）の場合はvnode.appContext.providesから取得（#13212） | カスタム要素内 |
| BR-25-04 | ルートインスタンス | ルートインスタンス（parent == null）の場合はappContext.providesから取得 | ルートコンポーネント内 |
| BR-25-05 | ファクトリデフォルト | treatDefaultAsFactory=trueかつdefaultValueがfunctionの場合、instance.proxyをthisとして呼び出す | inject呼び出し時 |
| BR-25-06 | setup限定 | provide()はsetup()内でのみ呼び出し可能。inject()はsetup()または関数型コンポーネント内で呼び出し可能 | API呼び出し時 |

### 計算ロジック

該当なし。

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

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

該当なし（クライアントサイドフレームワーク機能）。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 開発警告 | provide()をsetup()外で呼び出した場合 | `provide() can only be used inside setup().`警告 |
| - | 開発警告 | inject()をsetup()外・関数型コンポーネント外で呼び出した場合 | `inject() can only be used inside setup() or functional components.`警告 |
| - | 開発警告 | inject()で指定キーの値が見つからずデフォルト値も未指定の場合 | `injection "${key}" not found.`警告 |

### リトライ仕様

リトライ機構は存在しない。

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

該当なし。

## パフォーマンス要件

- プロトタイプチェーンを使用するため、inject()のルックアップはO(depth)（コンポーネントツリーの深さ）
- provide()は初回呼び出し時のみObject.create()を行い、2回目以降は既存オブジェクトへの直接代入のためO(1)

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

- provide/injectで共有されるデータに機密情報が含まれる場合、意図しない子孫コンポーネントからのアクセスに注意が必要

## 備考

- `InjectionKey<T>`はsymbol型でジェネリクスによる型安全な注入を実現
- `hasInjectionContext()`はVue Routerの`useRoute()`等のライブラリ内部で使用され、inject可能なコンテキスト外での呼び出し警告を抑制する
- Options APIの`provide`/`inject`オプションに対応するComposition API

---

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

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

### 推奨読解順序

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

InjectionKey型とprovidesオブジェクトの構造を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | apiInject.ts | `packages/runtime-core/src/apiInject.ts` | `InjectionKey<T>`型（8行目）。`symbol & InjectionConstraint<T>`で型安全を実現 |

**読解のコツ**: `InjectionKey<T>`は`symbol`型にジェネリクスを組み合わせた型であり、TypeScriptの型推論と組み合わせて`provide(key, value)`と`inject(key)`間で型の一貫性を保証する。

#### Step 2: provide()の実装を理解する

provide関数のプロトタイプチェーン生成ロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | apiInject.ts | `packages/runtime-core/src/apiInject.ts` | provide関数（10-34行目） |

**主要処理フロー**:
1. **14-18行目**: 開発モードで`currentInstance`の存在とマウント前であることを確認
2. **19-20行目**: `currentInstance.provides`を取得
3. **26-29行目**: `parentProvides === provides`（親のprovidesと同一の場合）、`Object.create(parentProvides)`でプロトタイプチェーンを利用した新オブジェクトを生成
4. **32行目**: `provides[key as string] = value`で値を設定

#### Step 3: inject()の実装を理解する

inject関数のprovidesチェーン走査ロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | apiInject.ts | `packages/runtime-core/src/apiInject.ts` | inject関数（47-85行目） |

**主要処理フロー**:
- **54行目**: `getCurrentInstance()`でインスタンス取得（関数型コンポーネント対応のためcurrentRenderingInstanceもフォールバック）
- **57行目**: `instance || currentApp`でコンテキスト存在確認
- **64-70行目**: providesオブジェクトの決定ロジック（currentApp優先、ルート/カスタム要素の特別処理）
- **72-74行目**: `key in provides`でキー存在確認、存在すれば返却
- **75-78行目**: デフォルト値処理。`treatDefaultAsFactory && isFunction(defaultValue)`の場合はファクトリ呼び出し

#### Step 4: hasInjectionContext()を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | apiInject.ts | `packages/runtime-core/src/apiInject.ts` | hasInjectionContext関数（92-94行目） |

**主要処理フロー**:
- **93行目**: `return !!(getCurrentInstance() || currentApp)` - インスタンスまたはcurrentAppが存在するかチェック

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

```
provide(key, value)
    │
    ├─ currentInstance.provides 取得
    │
    ├─ parentProvides === provides チェック
    │      └─ Object.create(parentProvides) で新オブジェクト生成
    │
    └─ provides[key] = value

inject(key, defaultValue, treatDefaultAsFactory)
    │
    ├─ getCurrentInstance() / currentApp
    │
    ├─ provides オブジェクト決定
    │      ├─ currentApp._context.provides
    │      ├─ vnode.appContext.provides（ルート/CE）
    │      └─ instance.parent.provides
    │
    ├─ key in provides → provides[key] 返却
    │
    └─ デフォルト値処理
           ├─ ファクトリ: defaultValue.call(instance.proxy)
           └─ 直接値: defaultValue 返却

hasInjectionContext()
    └─ !!(getCurrentInstance() || currentApp)
```

### データフロー図

```
[provide側]                 [プロトタイプチェーン]            [inject側]

ParentComponent             parentProvides                  ChildComponent
  provide(key, value) ──▶   { key: value }                  inject(key)
                              ↑ prototype                     │
GrandParentComponent        grandParentProvides               ▼
  provide(key2, value2) ──▶  { key2: value2 }              key in provides?
                              ↑ prototype                   ──▶ provides[key]
AppContext                  appContext.provides
  app.provide(key3, v3) ──▶  { key3: value3 }
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| apiInject.ts | `packages/runtime-core/src/apiInject.ts` | ソース | provide/inject/hasInjectionContext関数、InjectionKey型 |
| apiCreateApp.ts | `packages/runtime-core/src/apiCreateApp.ts` | ソース | app.provide()実装、currentApp変数 |
| component.ts | `packages/runtime-core/src/component.ts` | ソース | ComponentInternalInstanceのprovidesプロパティ |
| componentOptions.ts | `packages/runtime-core/src/componentOptions.ts` | ソース | Options APIのprovide/injectオプション処理 |
