# 通知設計書 9-リアクティビティ変更通知（notify）

## 概要

本ドキュメントは、Vue.jsリアクティビティシステムにおけるデータ変更通知機構（`Dep.notify()`メソッド）の設計仕様を記述する。`packages/reactivity/src/dep.ts`に実装されており、リアクティブなデータの変更時に依存する全てのエフェクト（computed、watcher、レンダリング関数等）にバッチ方式で変更を伝播する。Vueリアクティビティシステムの根幹をなす内部通知機構である。

### 本通知の処理概要

この通知は、リアクティブデータ（`ref`、`reactive`等）が変更された際に、そのデータに依存する全てのサブスクライバー（エフェクト）にバッチ方式で変更を伝播する機構である。`Dep`クラスのサブスクライバーリスト（双方向リンクリスト）を走査し、各サブスクライバーの`notify()`メソッドを呼び出す。computedの場合は連鎖的にその依存先にも通知を伝播する。

**業務上の目的・背景**：Vue.jsのリアクティビティシステムは、データの変更を自動的にUI更新に反映するフレームワークの根幹機能である。開発者がリアクティブデータを変更すると、そのデータに依存するcomputed、watcher、コンポーネントレンダリング関数などが自動的に再実行される。この「変更検知→エフェクト再実行」のメカニズムの中核が`Dep.notify()`である。バッチ処理（`startBatch`/`endBatch`）により、同一ティック内の複数の変更をまとめて処理し、不要な再計算を防止する。

**通知の送信タイミング**：リアクティブプロパティへの代入（SET）、プロパティの追加（ADD）、プロパティの削除（DELETE）、コレクションのクリア（CLEAR）の各操作時に`trigger()`関数経由で発火する。`Dep.trigger()`は`version`と`globalVersion`をインクリメントした後に`Dep.notify()`を呼び出す。

**通知の受信者**：Depに登録されたサブスクライバー（`Subscriber`インターフェース実装）。具体的にはReactiveEffect（watch、watchEffect）、ComputedRefImpl（computed）、コンポーネントの更新エフェクト。

**通知内容の概要**：変更が発生したことの通知（バイナリ通知）。開発モードでは`DebuggerEventExtraInfo`（target、type、key、newValue、oldValue、oldTarget）のデバッグ情報が付加される。

**期待されるアクション**：サブスクライバーがバッチキューに追加され、バッチ終了時（`endBatch`）に実行される。computedの場合は遅延再計算、watcherの場合はコールバック実行、レンダリングエフェクトの場合はコンポーネント再レンダリングが行われる。

## 通知種別

内部通知（リアクティビティシステム内部のサブスクライバーへの伝播）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（バッチ処理付き） |
| 優先度 | 高（データ変更の即時反映） |
| リトライ | なし |

### 送信先決定ロジック

`Dep`インスタンスに登録されたサブスクライバー（`subs`双方向リンクリスト）を末尾から先頭方向に走査し、各サブスクライバーの`notify()`メソッドを呼び出す。サブスクライバーがcomputedの場合（`notify()`がtrueを返す）、そのcomputedのDepにも連鎖的にnotifyを発行する。

## 通知テンプレート

### 内部通知の場合

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | N/A（内部通知） |
| 送信元名称 | Dep（依存関係追跡ノード） |
| 件名 | N/A |
| 形式 | メソッド呼び出し（Subscriber.notify()） |

### 本文テンプレート

```
// 内部通知（外部出力なし）
subscriber.notify()  // → true（computed）or void（effect）
```

開発モードでは `onTrigger` デバッグフックが呼び出される：
```javascript
subscriber.onTrigger({
  effect: subscriber,
  target: object,
  type: TriggerOpTypes,
  key: unknown,
  newValue: unknown,
  oldValue: unknown,
  oldTarget: Map | Set
})
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| N/A | N/A | N/A | 内部通知のため添付ファイルなし |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| debugInfo | デバッグイベント情報 | trigger()関数の引数（__DEV__時のみ） | No |
| subs | サブスクライバーリスト（末尾） | Dep.subs（双方向リンクリスト） | Yes |
| subsHead | サブスクライバーリスト（先頭） | Dep.subsHead（__DEV__時のみ） | No |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| プロパティ変更 | reactiveオブジェクトのプロパティ代入 | targetMapにDepが登録済み | SET操作 |
| プロパティ追加 | reactiveオブジェクトへの新プロパティ追加 | 同上 | ADD操作（配列のlength変更も含む） |
| プロパティ削除 | reactiveオブジェクトのプロパティ削除 | 同上 | DELETE操作 |
| コレクションクリア | Map/Setのclear操作 | 同上 | CLEAR操作（全Depをトリガー） |
| Ref変更 | ref.value への代入 | Dep.trigger()が呼ばれた場合 | Refの直接変更 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| targetMapにDepが未登録 | 一度もtrackされていないオブジェクトへの変更 |
| サブスクライバーがNOTIFIED状態 | 既にバッチキューに追加済みの場合（EffectFlags.NOTIFIED） |
| サブスクライバーがRUNNING状態かつALLOW_RECURSEなし | 実行中のエフェクトが再帰呼び出しを許可していない場合 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[リアクティブデータ変更] --> B["trigger(target, type, key, ...)"]
    B --> C{targetMapにDepが<br>登録されているか?}
    C -->|No| D[globalVersion++のみ]
    C -->|Yes| E[startBatch]
    E --> F{type === CLEAR?}
    F -->|Yes| G[全Depのtriggerを実行]
    F -->|No| H[該当keyのDepを取得]
    H --> I["dep.trigger(debugInfo)"]
    I --> J[dep.version++ / globalVersion++]
    J --> K["dep.notify(debugInfo)"]
    K --> L[startBatch]
    L --> M{__DEV__?}
    M -->|Yes| N[subsHeadから順方向にonTriggerフック実行]
    M -->|No| O[subsから逆方向にサブスクライバー走査]
    N --> O
    O --> P{link.sub.notify()がtrueを返すか?}
    P -->|Yes: computed| Q[computed.dep.notify()を連鎖呼び出し]
    P -->|No: effect| R[次のサブスクライバーへ]
    Q --> R
    R --> S[endBatch]
    S --> T{batchDepth === 0?}
    T -->|Yes| U[バッチキュー内のエフェクトを順次実行]
    T -->|No| V[外側のバッチ終了を待つ]
    G --> W[endBatch]
```

## データベース参照・更新仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| N/A | N/A | データベースは使用しない |

### テーブル別参照項目詳細

本通知はデータベースを使用しない。以下のインメモリデータ構造を参照する。

| 参照データ | 用途 | 取得方法 |
|-----------|------|---------|
| targetMap (WeakMap) | リアクティブオブジェクト→キー→Depのマッピング | グローバル変数 |
| Dep.subs (Link) | サブスクライバーの双方向リンクリスト（末尾） | Depインスタンスプロパティ |
| Dep.subsHead (Link) | サブスクライバーの双方向リンクリスト（先頭、__DEV__のみ） | Depインスタンスプロパティ |
| Dep.version (number) | 依存関係のバージョン | Depインスタンスプロパティ |
| globalVersion (number) | グローバルバージョン（computed最適化用） | モジュール変数 |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| Dep.version | UPDATE | trigger時にインクリメント |
| globalVersion | UPDATE | trigger時にインクリメント |
| batchedSub / batchedComputed | UPDATE | notifyされたサブスクライバーをバッチキューに追加 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| エフェクト実行エラー | endBatch内でエフェクトのtrigger()が例外を投げた場合 | 最初のエラーを記録し、全エフェクト実行後にthrow |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（リトライなし） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | なし（フレームワーク内部通知のため制限不要） |
| 1日あたり上限 | なし |

### 配信時間帯

制限なし。リアクティブデータの変更に即座に反応する。

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

- フレームワーク内部の通知機構であり、外部への情報漏洩リスクはない
- デバッグ情報（target、key、newValue、oldValue）は`__DEV__`モードでのみ生成される

## 備考

- `Dep`クラスは`Link`ノードによる双方向リンクリストでサブスクライバーを管理する（メモリ効率のためSetではなくリンクリストを採用）
- `globalVersion`はcomputedの高速パスに使用される。リアクティブ変更がなければcomputedは再計算をスキップできる
- `ITERATE_KEY`、`MAP_KEY_ITERATE_KEY`、`ARRAY_ITERATE_KEY`はイテレーション系の特殊キーとしてSymbolで定義されている
- `notify()`内でcomputedの場合は連鎖通知を行う（コールスタック深度削減のため、computed内部ではなくDep.notify()内で処理）
- バッチ処理は`startBatch`/`endBatch`でネスト可能であり、最も外側のendBatchで全てのエフェクトが実行される
- `addSub`関数ではcomputedの初回サブスクライバー登録時に、computed自身のdepsにも再帰的にサブスクリプションを追加する

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | dep.ts | `packages/reactivity/src/dep.ts` | L32-62: Link クラス。sub(Subscriber)とdep(Dep)を結ぶ双方向リンクリストのノード |
| 1-2 | dep.ts | `packages/reactivity/src/dep.ts` | L67-106: Dep クラスのプロパティ定義。version, activeLink, subs, subsHead, map, key, sc |
| 1-3 | dep.ts | `packages/reactivity/src/dep.ts` | L240: targetMap（WeakMap: object → Map<key, Dep>）のグローバル定義 |
| 1-4 | effect.ts | `packages/reactivity/src/effect.ts` | L58-83: Subscriber インターフェース。deps, depsTail, flags, next, notify()メソッド |

**読解のコツ**: DepとSubscriberはLink経由で多対多の関係を持つ。Linkはその両方への参照と、prevSub/nextSub/prevDep/nextDepで双方向リンクリストを形成する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | dep.ts | `packages/reactivity/src/dep.ts` | L294-389: trigger関数。外部からの変更トリガー（SET/ADD/DELETE/CLEAR）を処理し、該当するDepのtriggerを呼ぶ |
| 2-2 | dep.ts | `packages/reactivity/src/dep.ts` | L167-171: Dep.trigger()メソッド。version++, globalVersion++してnotify()を呼ぶ |

**主要処理フロー**:
1. **L302-307**: `targetMap.get(target)` でDepMapを取得（未登録なら`globalVersion++`のみ）
2. **L309-324**: 変更種別に応じて該当Depの`trigger()`を呼ぶ（`run`ヘルパー経由）
3. **L328-388**: 配列のlength変更、ITERATE_KEY、MAP_KEY_ITERATE_KEY等の特殊ケース処理
4. **L168-170**: `this.version++; globalVersion++; this.notify(debugInfo);`

#### Step 3: 通知メカニズムを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | dep.ts | `packages/reactivity/src/dep.ts` | L173-204: Dep.notify()メソッド。バッチ開始、onTriggerフック、サブスクライバー走査、computed連鎖、バッチ終了 |

**主要処理フロー**:
- **L174**: `startBatch()` でバッチカウンタをインクリメント
- **L176-191**: `__DEV__`時、subsHeadから順方向にonTriggerフックを呼び出し（正しい順序で実行するため）
- **L193-200**: subsから逆方向（末尾→先頭）にサブスクライバーを走査し、`link.sub.notify()`を呼ぶ
- **L194-198**: computedの場合（`notify()`がtrueを返す）、`computed.dep.notify()`を連鎖呼び出し
- **L202**: `endBatch()` でバッチカウンタをデクリメントし、0になったらキューを実行

#### Step 4: バッチ処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | effect.ts | `packages/reactivity/src/effect.ts` | L236-299: batch, startBatch, endBatch関数。エフェクトのバッチキュー管理と実行 |

**主要処理フロー**:
- **L240-248**: `batch()` - サブスクライバーにNOTIFIEDフラグを立て、batchedSub/batchedComputedリストに追加
- **L254-256**: `startBatch()` - batchDepthをインクリメント
- **L262-299**: `endBatch()` - batchDepthが0になったら、batchedComputed→batchedSubの順でキューを処理

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

```
[リアクティブデータ変更]
    |
    +-- trigger(target, type, key, newValue, oldValue)   [dep.ts:294]
            |
            +-- targetMap.get(target)                     [dep.ts:302]
            +-- dep.trigger(debugInfo)                    [dep.ts:167]
                    |
                    +-- version++ / globalVersion++        [dep.ts:168-169]
                    +-- dep.notify(debugInfo)               [dep.ts:173]
                            |
                            +-- startBatch()               [effect.ts:254]
                            +-- [__DEV__] onTriggerフック走査  [dep.ts:176-191]
                            +-- サブスクライバー走査 (subs)  [dep.ts:193]
                            |       |
                            |       +-- link.sub.notify()  [Subscriber.notify]
                            |       |       |
                            |       |       +-- [ReactiveEffect] batch(this) [effect.ts:240]
                            |       |       +-- [ComputedRefImpl] return true
                            |       |
                            |       +-- [computed] computed.dep.notify() [連鎖]
                            |
                            +-- endBatch()                 [effect.ts:262]
                                    |
                                    +-- batchedComputed処理
                                    +-- batchedSub処理
                                            |
                                            +-- effect.trigger() [effect.ts:195]
                                                    |
                                                    +-- scheduler() or runIfDirty()
```

### データフロー図

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

target (object)            +-- trigger()                       +-- Subscriber.notify()
  |                        |     |                             |     (バッチキュー追加)
type (TriggerOpTypes)      |     +-- targetMap参照             |
  |                   ---->|     +-- Dep.trigger()         --->+-- computed連鎖通知
key (unknown)              |     +-- Dep.notify()              |     (dep.notify()再帰)
  |                        |           |                       |
newValue / oldValue   ---->|           +-- startBatch()        +-- endBatch()
                           |           +-- サブスクライバー走査  |     (エフェクト実行)
                           |           +-- endBatch()          |
                           +--                                 +-- UI更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| dep.ts | `packages/reactivity/src/dep.ts` | ソース | Dep/Linkクラス、track/trigger関数、targetMap |
| effect.ts | `packages/reactivity/src/effect.ts` | ソース | ReactiveEffect、Subscriberインターフェース、batch/startBatch/endBatch |
| computed.ts | `packages/reactivity/src/computed.ts` | ソース | ComputedRefImpl（computedのSubscriber実装） |
| reactive.ts | `packages/reactivity/src/reactive.ts` | ソース | reactiveプロキシの作成 |
| ref.ts | `packages/reactivity/src/ref.ts` | ソース | ref/shallowRefの実装（Dep.trigger呼び出し元） |
| baseHandlers.ts | `packages/reactivity/src/baseHandlers.ts` | ソース | Proxyハンドラ（set/deleteProperty時にtrigger呼び出し） |
| constants.ts | `packages/reactivity/src/constants.ts` | ソース | TrackOpTypes/TriggerOpTypesの定義 |
