# 機能設計書 9-リアクティブ判定ユーティリティ

## 概要

本ドキュメントは、Vue 3のリアクティビティシステムにおける判定ユーティリティ群の機能設計を記述する。`isProxy()`, `isReactive()`, `isReadonly()`, `isShallow()`等のリアクティブ状態判定関数、および`toRaw()`, `markRaw()`のプロキシ操作関数を含む。

### 本機能の処理概要

リアクティブ判定ユーティリティは、オブジェクトのリアクティブ状態を検査するための関数群と、プロキシの自動変換を制御するための関数群を提供する。各関数はReactiveFlagsの内部フラグを参照して判定を行う。

**業務上の目的・背景**：リアクティブシステムの状態を外部から検査・制御するためのユーティリティである。コンポーネント開発やライブラリ開発において、オブジェクトのリアクティブ状態に応じた処理分岐が必要な場面で使用される。toRaw()は一時的なプロキシオーバーヘッド回避に、markRaw()は大量データやサードパーティオブジェクトのリアクティブ変換防止に有用である。

**機能の利用シーン**：コンポーネントの状態管理でリアクティブ/非リアクティブの判定、テスト時のオブジェクト状態確認、サードパーティライブラリオブジェクトのリアクティブ変換防止、パフォーマンス最適化のための一時的なプロキシ解除。

**主要な処理内容**：
1. `isProxy(value)`: ReactiveFlags.RAWの存在でプロキシ判定
2. `isReactive(value)`: IS_REACTIVEフラグで判定。readonlyの場合はRAWを再帰的に確認
3. `isReadonly(value)`: IS_READONLYフラグで判定
4. `isShallow(value)`: IS_SHALLOWフラグで判定
5. `isRef(value)`: IS_REFフラグで判定
6. `toRaw(value)`: RAWフラグで再帰的に元のオブジェクトを取得
7. `markRaw(value)`: SKIPフラグを設定してリアクティブ変換対象外にする

**関連システム・外部連携**：`@vue/reactivity`パッケージに所属。reactive.ts、ref.tsに定義され、リアクティビティシステム全体で使用される。

**権限による制御**：特になし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 画面機能マッピングにリアクティブ判定ユーティリティ単体の関連画面は登録されていない |

## 機能種別

リアクティビティ管理 / ユーティリティAPI

## 入力仕様

### 入力パラメータ

#### 判定関数群

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| value | unknown | Yes | 判定対象の値 | なし |

#### toRaw()

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| observed | T | Yes | プロキシオブジェクト | なし |

#### markRaw()

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| value | T extends object | Yes | リアクティブ変換対象外にするオブジェクト | オブジェクトであること |

### 入力データソース

APIの引数として直接渡される。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| isProxy() | boolean | プロキシであればtrue |
| isReactive() | boolean | リアクティブプロキシであればtrue |
| isReadonly() | boolean | 読み取り専用プロキシであればtrue |
| isShallow() | boolean | 浅いプロキシであればtrue |
| isRef() | boolean | Refであればtrue |
| toRaw() | T | 元のオブジェクト |
| markRaw() | Raw\<T\> | SKIPフラグ付きのオブジェクト |

### 出力先

呼び出し元に返却。

## 処理フロー

### 処理シーケンス

```
isProxy(value):
  └─ value[ReactiveFlags.RAW]の存在判定 → boolean

isReactive(value):
  ├─ isReadonly(value)の場合: value[RAW]を再帰的にisReactive
  └─ value[IS_REACTIVE]の存在判定 → boolean

isReadonly(value):
  └─ value[IS_READONLY]の存在判定 → boolean

isShallow(value):
  └─ value[IS_SHALLOW]の存在判定 → boolean

isRef(value):
  └─ value[IS_REF] === true → boolean

toRaw(value):
  ├─ value[RAW]を取得
  └─ 取得できればtoRaw()を再帰呼び出し、なければvalueを返却

markRaw(value):
  ├─ SKIPフラグ未設定かつ拡張可能な場合
  └─ def(value, ReactiveFlags.SKIP, true) → value返却
```

### フローチャート

```mermaid
flowchart TD
    A{どの関数?} -->|isProxy| B[value.RAW 存在?]
    B -->|Yes| C[true]
    B -->|No| D[false]
    A -->|isReactive| E{isReadonly value?}
    E -->|Yes| F[isReactive value.RAW]
    E -->|No| G[value.IS_REACTIVE?]
    G -->|Yes| H[true]
    G -->|No| I[false]
    A -->|toRaw| J{value.RAW 存在?}
    J -->|Yes| K[toRaw value.RAW 再帰]
    J -->|No| L[value返却]
    A -->|markRaw| M[def value, SKIP, true]
    M --> N[value返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | isReactiveのreadonly対応 | readonlyプロキシの場合、内部のRAWオブジェクトのisReactiveを再帰判定 | readonly(reactive(obj))の場合 |
| BR-02 | toRawの再帰解決 | 多重プロキシ（readonly(reactive(obj))）の場合、全レイヤーを剥がして元のオブジェクトを返す | 常時 |
| BR-03 | markRawの条件 | SKIPフラグが未設定かつObject.isExtensible()がtrueの場合のみSKIPフラグを設定 | 常時 |
| BR-04 | TypeScript型ガード | isRef()はis Ref<T>型ガード関数として機能する | TypeScriptでの型絞り込み |

### 計算ロジック

ReactiveFlags列挙型の定義:
- `SKIP` = '__v_skip' - リアクティブ変換スキップ
- `IS_REACTIVE` = '__v_isReactive' - reactive判定
- `IS_READONLY` = '__v_isReadonly' - readonly判定
- `IS_SHALLOW` = '__v_isShallow' - shallow判定
- `RAW` = '__v_raw' - 元オブジェクト参照
- `IS_REF` = '__v_isRef' - ref判定

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

該当なし

## エラー処理

### エラーケース一覧

該当なし（判定関数のためエラーは発生しない）

### リトライ仕様

該当なし

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

該当なし

## パフォーマンス要件

- 全ての判定関数はO(1)の計算量（フラグ参照のみ）
- toRaw()の再帰はプロキシネスト深度に比例（通常1-2レイヤー）
- `@__NO_SIDE_EFFECTS__`アノテーションでツリーシェイキング対応

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

toRaw()でProxyを剥がすと、依存追跡やreadonly保護がバイパスされる。セキュリティ境界としての使用は推奨されない。

## 備考

- `@__NO_SIDE_EFFECTS__`アノテーションにより、未使用の場合はバンドルから除外される
- isReactive(readonly(reactive(obj)))はtrueを返す（内部のreactiveを検出）
- isRef()はref.tsに定義され、IS_REFフラグで判定する
- toReactive()、toReadonly()はそれぞれオブジェクトのみ変換するヘルパー

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | constants.ts | `packages/reactivity/src/constants.ts` | ReactiveFlags列挙型（17-24行目）。全フラグ定義 |
| 1-2 | reactive.ts | `packages/reactivity/src/reactive.ts` | Targetインターフェース（18-24行目）。フラグのオプショナルプロパティ定義 |

**読解のコツ**: 各判定関数は対象オブジェクトの特定のフラグプロパティ（文字列キー）を参照するだけのシンプルな実装。Proxyのgetトラップがこれらのフラグアクセスをインターセプトして適切な値を返す仕組みになっている。

#### Step 2: 判定関数群を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | reactive.ts | `packages/reactivity/src/reactive.ts` | isProxy()（359-361行目）。RAWフラグによる判定 |
| 2-2 | reactive.ts | `packages/reactivity/src/reactive.ts` | isReactive()（323-328行目）。IS_REACTIVEフラグ + readonlyの再帰判定 |
| 2-3 | reactive.ts | `packages/reactivity/src/reactive.ts` | isReadonly()（342-344行目）。IS_READONLYフラグ |
| 2-4 | reactive.ts | `packages/reactivity/src/reactive.ts` | isShallow()（347-349行目）。IS_SHALLOWフラグ |
| 2-5 | ref.ts | `packages/reactivity/src/ref.ts` | isRef()（45-49行目）。IS_REFフラグ |

**主要処理フロー**:
- **323-328行目**: isReactive()。readonlyの場合はRAWを再帰的に確認
- **359-361行目**: isProxy()。RAWプロパティの存在判定（!!value[RAW]）

#### Step 3: プロキシ操作関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | reactive.ts | `packages/reactivity/src/reactive.ts` | toRaw()（387-390行目）。再帰的なプロキシ解除 |
| 3-2 | reactive.ts | `packages/reactivity/src/reactive.ts` | markRaw()（416-421行目）。SKIPフラグの設定 |

**主要処理フロー**:
- **387-390行目**: toRaw()。`const raw = observed[RAW]`を取得し、存在すればtoRaw(raw)を再帰呼び出し
- **416-421行目**: markRaw()。SKIPフラグ未設定かつ拡張可能な場合、`def(value, SKIP, true)`

#### Step 4: Proxyハンドラでのフラグ返却を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | baseHandlers.ts | `packages/reactivity/src/baseHandlers.ts` | BaseReactiveHandler.get()（55-85行目）。IS_REACTIVE/IS_READONLY/IS_SHALLOW/RAWの返却ロジック |

**主要処理フロー**:
- **60-61行目**: IS_REACTIVEアクセス → !isReadonlyを返却
- **62-63行目**: IS_READONLYアクセス → isReadonlyを返却
- **64-65行目**: IS_SHALLOWアクセス → isShallowを返却
- **66-85行目**: RAWアクセス → receiverが正しいProxyの場合にtarget（元オブジェクト）を返却

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

```
isProxy(value) ──> value[ReactiveFlags.RAW] ──> boolean
    |
    +-- [Proxy get trap] BaseReactiveHandler.get()
            +-- key === RAW → return target

isReactive(value)
    |
    +-- isReadonly(value) ?
    |       +-- isReactive(value[RAW])  // 再帰
    +-- value[IS_REACTIVE]
            +-- [Proxy get trap] → return !isReadonly

toRaw(observed)
    |
    +-- observed[RAW] → raw
            +-- raw ? toRaw(raw) : observed  // 再帰的解決

markRaw(value)
    |
    +-- hasOwn(value, SKIP) ?
    +-- Object.isExtensible(value) ?
    +-- def(value, SKIP, true)
```

### データフロー図

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

value (any)     ───>  isProxy/isReactive/...       ───>  boolean
                          |
                    ReactiveFlags参照
                          |
                    Proxy getトラップ経由
                          |
                    フラグ値返却

observed (Proxy) ───> toRaw(observed)              ───>  元のobject
                          |
                    value[RAW]で再帰的解決

value (object)  ───> markRaw(value)                ───>  Raw<T>
                          |
                    def(value, SKIP, true)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| reactive.ts | `packages/reactivity/src/reactive.ts` | ソース | isProxy, isReactive, isReadonly, isShallow, toRaw, markRaw |
| ref.ts | `packages/reactivity/src/ref.ts` | ソース | isRef |
| constants.ts | `packages/reactivity/src/constants.ts` | ソース | ReactiveFlags列挙型 |
| baseHandlers.ts | `packages/reactivity/src/baseHandlers.ts` | ソース | Proxyハンドラでのフラグ返却ロジック |
| index.ts | `packages/reactivity/src/index.ts` | ソース | パッケージエクスポート定義 |
