# 機能設計書 2-reactive()

## 概要

本ドキュメントは、Vue 3のリアクティビティシステムにおける`reactive()` APIの機能設計を記述する。`reactive()`はオブジェクトや配列をProxyベースのリアクティブプロキシに変換し、プロパティの読み取り・書き込み・削除を自動的に追跡するAPIである。

### 本機能の処理概要

`reactive()`は、対象オブジェクトに対してES6 Proxyを用いたリアクティブプロキシを生成し、プロパティアクセス時の依存性追跡とプロパティ変更時のエフェクトトリガーを自動化する。ネストされたオブジェクトも再帰的にリアクティブに変換される（深いリアクティビティ）。

**業務上の目的・背景**：コンポーネントの複雑な状態（オブジェクト・配列・Map・Set等）をリアクティブに管理するために不可欠なAPIである。ref()がプリミティブ値を主な対象とするのに対し、reactive()はオブジェクト全体をリアクティブにすることで、`.value`なしでプロパティに直接アクセスできる利便性を提供する。

**機能の利用シーン**：複数のプロパティを持つ状態オブジェクトの定義、フォームデータの管理、コンポーネント間の共有状態管理など、構造化されたデータをリアクティブに扱う場面で使用される。

**主要な処理内容**：
1. 対象オブジェクトの型判定（Object, Array, Map, Set, WeakMap, WeakSet）
2. 対象オブジェクトに対応するProxyHandlerの選択（通常 vs コレクション）
3. WeakMapベースのキャッシュで同一オブジェクトのProxy重複生成を防止
4. Proxyのgetハンドラでtrack()を呼び出し依存性を記録
5. Proxyのsetハンドラでtrigger()を呼び出しエフェクトを通知
6. ネストされたオブジェクトへのアクセス時に遅延的にリアクティブプロキシを生成

**関連システム・外部連携**：`@vue/reactivity`パッケージに所属。baseHandlers.ts（通常オブジェクト用ハンドラ）、collectionHandlers.ts（Map/Set用ハンドラ）、dep.ts（依存性管理）と連携する。

**権限による制御**：特に権限制御はない。`shallowReactive()`で浅いリアクティビティに制限可能。readonlyプロキシが既に適用されている場合はそのまま返却する。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 5 | Options Panel（コンパイルオプション） | 主画面 | compilerOptions（mode/whitespace/hoistStatic等）のリアクティブオブジェクト管理 |

## 機能種別

リアクティビティ管理 / 状態管理API

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| target | T extends object | Yes | リアクティブプロキシに変換する対象オブジェクト | オブジェクト型であること。プリミティブ値は開発モードで警告 |

### 入力データソース

APIの引数として直接渡されるオブジェクト。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| Reactive\<T\> | UnwrapNestedRefs\<T\> | 深くリアクティブに変換されたプロキシオブジェクト |

### 出力先

呼び出し元のComposition APIコンテキストに返却。

## 処理フロー

### 処理シーケンス

```
1. reactive(target)が呼び出される
   └─ 対象オブジェクトを受け取る
2. readonlyプロキシかどうか判定
   └─ readonlyの場合: そのまま返却
3. createReactiveObject()を呼び出す
   ├─ isObject(target)チェック → 非オブジェクトは警告して返却
   ├─ 既にProxyの場合: そのまま返却（readonlyをreactiveにラップする場合は例外）
   ├─ TargetType判定: COMMON(Object/Array) or COLLECTION(Map/Set) or INVALID
   ├─ proxyMap(WeakMap)で既存Proxy検索
   └─ 新規Proxy生成: TargetTypeに応じたハンドラを選択
4. proxyMapに登録してプロキシを返却
```

### フローチャート

```mermaid
flowchart TD
    A[reactive target] --> B{isReadonly target ?}
    B -->|Yes| C[targetをそのまま返却]
    B -->|No| D[createReactiveObject]
    D --> E{isObject target ?}
    E -->|No| F[警告して返却]
    E -->|Yes| G{既にProxy?}
    G -->|Yes| H{readonly + reactive?}
    H -->|No| I[そのまま返却]
    H -->|Yes| J[続行]
    G -->|No| J
    J --> K[getTargetType]
    K --> L{TargetType?}
    L -->|INVALID| M[targetを返却]
    L -->|COMMON/COLLECTION| N{proxyMap既存?}
    N -->|Yes| O[既存Proxyを返却]
    N -->|No| P[new Proxy target, handlers]
    P --> Q[proxyMapに登録]
    Q --> R[Proxyを返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 非オブジェクト拒否 | プリミティブ値を渡した場合は警告を出し、値をそのまま返却 | !isObject(target)の場合 |
| BR-02 | 既存Proxy再利用 | 同一オブジェクトに対するProxyはWeakMapキャッシュから返却 | proxyMap.get(target)が存在する場合 |
| BR-03 | readonly保護 | readonlyプロキシが渡された場合はそのまま返却 | isReadonly(target)がtrueの場合 |
| BR-04 | SKIP対象除外 | markRaw()されたオブジェクトやObject.isExtensible()がfalseのオブジェクトはINVALIDとして返却 | ReactiveFlags.SKIPがtrueまたは拡張不可の場合 |
| BR-05 | 深いリアクティビティ | ネストされたオブジェクトプロパティへのアクセス時に遅延的にreactive()で変換 | getハンドラでisObject(res)がtrueの場合 |
| BR-06 | Ref自動アンラップ | リアクティブオブジェクト内のRefプロパティは自動的にアンラップされる | 配列の整数キー以外のRefプロパティアクセス時 |

### 計算ロジック

特になし（Proxyのトラップ処理は副作用的な動作）

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

該当なし（フロントエンドの状態管理API）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 開発者警告 | プリミティブ値をreactive()に渡した場合 | `value cannot be made reactive: {value}` |
| - | 開発者警告 | readonlyプロキシのプロパティをsetしようとした場合 | `Set operation on key "{key}" failed: target is readonly.` |
| - | 開発者警告 | readonlyプロキシのプロパティをdeleteしようとした場合 | `Delete operation on key "{key}" failed: target is readonly.` |

### リトライ仕様

該当なし

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

該当なし

## パフォーマンス要件

- WeakMapによるProxy重複生成防止でメモリ効率を確保
- ネストされたオブジェクトは遅延的に（アクセス時に初めて）リアクティブ化される
- shallowReactive()でルートレベルのみリアクティブ化し、パフォーマンスを最適化可能

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

特になし

## 備考

- `shallowReactive()`は浅いリアクティビティを提供し、ルートレベルのプロパティのみ追跡する
- Map, Set, WeakMap, WeakSetはコレクション専用のハンドラで処理される
- `markRaw()`で特定のオブジェクトをリアクティブ変換の対象外にできる
- reactive()内のRefプロパティは自動アンラップされるが、配列の整数キーアクセスでは除外される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | reactive.ts | `packages/reactivity/src/reactive.ts` | Target インターフェース（18-24行目）。ReactiveFlags による内部フラグ管理 |
| 1-2 | reactive.ts | `packages/reactivity/src/reactive.ts` | reactiveMap等4つのWeakMap（26-35行目）。Proxyキャッシュの構造 |
| 1-3 | reactive.ts | `packages/reactivity/src/reactive.ts` | TargetType列挙型（37-41行目）。INVALID/COMMON/COLLECTIONの分類 |

**読解のコツ**: reactive()は4種類のWeakMapキャッシュ（reactiveMap, shallowReactiveMap, readonlyMap, shallowReadonlyMap）を使い分ける。各マップはProxyの重複生成を防止し、同一オブジェクトに対して同一のProxyを返却する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | reactive.ts | `packages/reactivity/src/reactive.ts` | reactive()関数（91-105行目）。createReactiveObject()への委譲 |

**主要処理フロー**:
1. **93-97行目**: readonlyプロキシの場合はそのまま返却
2. **98-105行目**: createReactiveObject()を呼び出し。mutableHandlers、mutableCollectionHandlers、reactiveMapを渡す

#### Step 3: Proxyオブジェクト生成の内部実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | reactive.ts | `packages/reactivity/src/reactive.ts` | createReactiveObject()（261-302行目）。Proxy生成の中核ロジック |

**主要処理フロー**:
- **268-277行目**: 非オブジェクトチェック
- **280-285行目**: 既存Proxy判定（ReactiveFlags.RAW確認）
- **287-290行目**: TargetType判定。getTargetType()でSKIPフラグと拡張性を確認
- **292-295行目**: proxyMapから既存Proxy検索
- **296-301行目**: 新規Proxy生成。TargetTypeに応じてbaseHandlersまたはcollectionHandlersを使用

#### Step 4: Proxyハンドラの実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | baseHandlers.ts | `packages/reactivity/src/baseHandlers.ts` | BaseReactiveHandler.get()（55-134行目）。依存性追跡とネストオブジェクトの遅延変換 |
| 4-2 | baseHandlers.ts | `packages/reactivity/src/baseHandlers.ts` | MutableReactiveHandler.set()（142-192行目）。変更検知とトリガー |

**主要処理フロー**:
- **112-114行目**: get内でtrack(target, TrackOpTypes.GET, key)を呼び出し
- **120-131行目**: Refの自動アンラップとネストオブジェクトの遅延reactive化
- **184-190行目**: set内でtrigger()を呼び出し。ADD or SETの判定

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

```
reactive(target)
    |
    +-- createReactiveObject(target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap)
            |
            +-- isObject(target)             // 型チェック
            +-- getTargetType(target)        // TargetType判定
            +-- proxyMap.get(target)         // 既存Proxy検索
            +-- new Proxy(target, handlers)  // Proxy生成
            |       |
            |       +-- [get trap] BaseReactiveHandler.get()
            |       |       +-- track(target, type, key)   // 依存性追跡
            |       |       +-- reactive(res)              // ネストオブジェクトの遅延変換
            |       |
            |       +-- [set trap] MutableReactiveHandler.set()
            |       |       +-- trigger(target, type, key) // エフェクトトリガー
            |       |
            |       +-- [deleteProperty trap]
            |       +-- [has trap]
            |       +-- [ownKeys trap]
            |
            +-- proxyMap.set(target, proxy)  // キャッシュ登録
```

### データフロー図

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

target (object)  ───>  reactive(target)                ───>  Reactive<T> (Proxy)
                           |
                    createReactiveObject()
                           |
                    TargetType判定
                           |
                    +------+------+
                    |             |
              COMMON          COLLECTION
              (Object/Array)  (Map/Set/WeakMap/WeakSet)
                    |             |
              baseHandlers   collectionHandlers
                    |             |
              new Proxy()    new Proxy()
                    |             |
                    +------+------+
                           |
                    Reactive Proxy
                           |
                    +------+------+
                    |             |
              GET access    SET access
                    |             |
              track()       trigger()
                    |             |
              依存記録      エフェクト通知
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| reactive.ts | `packages/reactivity/src/reactive.ts` | ソース | reactive()のメイン実装。createReactiveObject、型判定ユーティリティ |
| baseHandlers.ts | `packages/reactivity/src/baseHandlers.ts` | ソース | Object/Array用のProxyハンドラ（get, set, deleteProperty, has, ownKeys） |
| collectionHandlers.ts | `packages/reactivity/src/collectionHandlers.ts` | ソース | Map/Set/WeakMap/WeakSet用のProxyハンドラ |
| dep.ts | `packages/reactivity/src/dep.ts` | ソース | track()/trigger()関数。依存性管理の中核 |
| constants.ts | `packages/reactivity/src/constants.ts` | ソース | ReactiveFlags, TrackOpTypes, TriggerOpTypes定義 |
| arrayInstrumentations.ts | `packages/reactivity/src/arrayInstrumentations.ts` | ソース | 配列メソッドのリアクティブ対応ラッパー |
| index.ts | `packages/reactivity/src/index.ts` | ソース | パッケージエクスポート定義 |
