# 機能設計書 7-effectScope()

## 概要

本ドキュメントは、Vue 3のリアクティビティシステムにおける`effectScope()` APIの機能設計を記述する。`effectScope()`は複数のリアクティブエフェクト（computed, watch等）をグループ化し、まとめて管理・破棄するためのスコープを生成するAPIである。

### 本機能の処理概要

`effectScope()`は`EffectScope`クラスのインスタンスを生成し、スコープ内で生成されたリアクティブエフェクトを自動的に収集する。`scope.stop()`を呼び出すことで、スコープ内の全エフェクトを一括で停止し、クリーンアップ関数を実行できる。子スコープのネスト、一時停止・再開にも対応する。

**業務上の目的・背景**：コンポーネントのセットアップ時に生成される複数のエフェクト（computed, watch, watchEffect）を、コンポーネントのアンマウント時にまとめて破棄する必要がある。effectScope()はこの「エフェクトの一括管理」パターンを抽象化し、Composable関数やストア（Pinia等）でのリソース管理を容易にする。

**機能の利用シーン**：Composable関数内でのリソース管理、Pinia等の状態管理ライブラリでのストアスコープ管理、テスト環境でのエフェクトクリーンアップ、カスタムレンダラーでのライフサイクル管理。

**主要な処理内容**：
1. EffectScopeインスタンスの生成（デタッチモード/アタッチモード）
2. run()でスコープをアクティブに設定し、コールバック内で生成されたエフェクトを自動収集
3. 子スコープのネスト管理（parent/scopes配列）
4. stop()でスコープ内の全エフェクト停止、クリーンアップ実行、子スコープの再帰停止
5. pause()/resume()でスコープ内の全エフェクトとサブスコープを一括制御
6. on()/off()でスコープのアクティブ状態を手動切替

**関連システム・外部連携**：`@vue/reactivity`パッケージに所属。ReactiveEffectのコンストラクタでactiveEffectScopeを参照してスコープ登録を行う。getCurrentScope()、onScopeDispose()と連携。

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 画面機能マッピングにeffectScope()単体の関連画面は登録されていない |

## 機能種別

リアクティビティ管理 / エフェクトライフサイクル管理API

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| detached | boolean | No | trueの場合、親スコープから独立したスコープを作成（デフォルト: false） | - |

### 入力データソース

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

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| EffectScope | EffectScope | run(), stop(), pause(), resume()メソッドを持つスコープオブジェクト |

### 出力先

呼び出し元に返却。

## 処理フロー

### 処理シーケンス

```
1. effectScope(detached?)が呼び出される
2. new EffectScope(detached)を生成
   ├─ parent = activeEffectScope を記録
   └─ detachedでない場合: parentのscopes配列に追加
3. scope.run(fn)が呼び出される
   ├─ activeEffectScope = this に設定
   ├─ fn()を実行（この間に生成されたReactiveEffectは自動的にeffects配列に追加）
   └─ activeEffectScope を復元
4. scope.stop()が呼び出される
   ├─ effects配列の全エフェクトをstop()
   ├─ cleanups配列の全クリーンアップ関数を実行
   ├─ 子scopes配列の全スコープをstop(true)
   └─ 親のscopes配列から自身を削除
```

### フローチャート

```mermaid
flowchart TD
    A[effectScope detached] --> B[new EffectScope detached]
    B --> C{detached?}
    C -->|No| D[parent.scopes.push this]
    C -->|Yes| E[独立スコープ]
    D --> F[scope.run fn]
    E --> F
    F --> G{_active?}
    G -->|Yes| H[activeEffectScope = this]
    G -->|No| I[警告]
    H --> J[fn 実行]
    J --> K[activeEffectScope 復元]
    K --> L[scope.stop]
    L --> M[effects全停止]
    M --> N[cleanups全実行]
    N --> O[子scopes全停止]
    O --> P[parentから削除]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 自動収集 | run()中に生成されたReactiveEffectは自動的にeffects配列に追加 | scope.run()実行中 |
| BR-02 | 再帰停止 | stop()は子スコープも再帰的に停止する | stop()呼び出し時 |
| BR-03 | detachedモード | detached=trueのスコープは親から独立し、親のstop()で自動停止されない | detached=true時 |
| BR-04 | O(1)削除 | 親のscopes配列からの削除は末尾要素とのスワップでO(1)で行われる | stop()でparentから削除時 |
| BR-05 | 一括一時停止 | pause()はスコープ内の全エフェクトと子スコープを一括で一時停止 | pause()呼び出し時 |
| BR-06 | 停止後実行不可 | stop()後のrun()は実行されない（開発モードで警告） | _active=false時 |

### 計算ロジック

特になし

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

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 開発者警告 | stop()済みのスコープでrun()を呼んだ場合 | `cannot run an inactive effect scope.` |
| - | 開発者警告 | activeEffectScope外でonScopeDispose()を呼んだ場合 | `onScopeDispose() is called when there is no active effect scope` |

### リトライ仕様

該当なし

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

該当なし

## パフォーマンス要件

- O(1)の子スコープ削除（末尾スワップ）
- effects/cleanups/scopes配列はlength=0でクリアしてGCを支援

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

特になし

## 備考

- `getCurrentScope()`で現在のアクティブスコープを取得可能
- `onScopeDispose(fn)`でスコープ停止時のクリーンアップを登録可能
- on()/off()はVue内部用（非detachedスコープ向け）。_onカウンターでネストされた呼び出しを管理
- コンポーネントのsetup()実行時にはコンポーネント固有のEffectScopeが自動的に設定される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | effectScope.ts | `packages/reactivity/src/effectScope.ts` | EffectScopeクラスのフィールド定義（6-51行目）。_active, effects, cleanups, parent, scopes, index |

**読解のコツ**: EffectScopeはツリー構造を形成する。parentは親スコープへの参照、scopesは子スコープの配列。detachedスコープは親ツリーに含まれない独立したスコープとなる。

#### Step 2: コンストラクタとスコープ登録を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | effectScope.ts | `packages/reactivity/src/effectScope.ts` | コンストラクタ（43-51行目）。parent設定と子スコープ配列への登録 |
| 2-2 | effect.ts | `packages/reactivity/src/effect.ts` | ReactiveEffectコンストラクタ（116-119行目）。activeEffectScopeへの自動登録 |

**主要処理フロー**:
- **43-51行目**: detachedでない場合、activeEffectScopeのscopes配列に自身を追加。index保存
- **116-119行目**: ReactiveEffect生成時、activeEffectScope.effects.push(this)

#### Step 3: run()とstop()を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | effectScope.ts | `packages/reactivity/src/effectScope.ts` | run()（92-104行目）。activeEffectScope切替とfn実行 |
| 3-2 | effectScope.ts | `packages/reactivity/src/effectScope.ts` | stop()（129-161行目）。全リソースのクリーンアップ |

**主要処理フロー**:
- **92-104行目**: run()。activeEffectScopeを自身に設定、fn()実行、復元
- **129-161行目**: stop()。effects停止 -> cleanups実行 -> 子scopes停止 -> parent.scopesから削除

#### Step 4: 関連ユーティリティを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | effectScope.ts | `packages/reactivity/src/effectScope.ts` | effectScope()（173-175行目）、getCurrentScope()（182-184行目）、onScopeDispose()（193-202行目） |

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

```
effectScope(detached?)
    |
    +-- new EffectScope(detached)
    |       +-- parent = activeEffectScope
    |       +-- !detached: parent.scopes.push(this)
    |
    +-- scope.run(fn)
    |       +-- activeEffectScope = this
    |       +-- fn()
    |       |       +-- effect() / computed() / watch()
    |       |               +-- ReactiveEffect constructor
    |       |                       +-- activeEffectScope.effects.push(this)
    |       +-- activeEffectScope = prev
    |
    +-- scope.stop()
            +-- effects.forEach(e => e.stop())
            +-- cleanups.forEach(fn => fn())
            +-- scopes.forEach(s => s.stop(true))
            +-- parent.scopes: O(1)スワップ削除
```

### データフロー図

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

detached: boolean ───> effectScope(detached)          ───> EffectScope
                           |
                    EffectScope生成
                           |
                    scope.run(fn)
                           |
                    activeEffectScopeに設定
                           |
                    fn()内のエフェクト生成
                           |
                    effects配列に自動収集
                           |
                    [scope.stop()]
                           |
                    全エフェクト停止
                    全クリーンアップ実行
                    子スコープ再帰停止
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| effectScope.ts | `packages/reactivity/src/effectScope.ts` | ソース | EffectScopeクラス、effectScope()、getCurrentScope()、onScopeDispose() |
| effect.ts | `packages/reactivity/src/effect.ts` | ソース | ReactiveEffectコンストラクタ（activeEffectScopeへの自動登録） |
| index.ts | `packages/reactivity/src/index.ts` | ソース | パッケージエクスポート定義 |
