# 機能設計書 19-onBeforeUpdate() / onUpdated()

## 概要

本ドキュメントは、リアクティブデータ変更によるDOM更新前後に実行されるライフサイクルフック `onBeforeUpdate()` と `onUpdated()` の機能設計書である。

### 本機能の処理概要

`onBeforeUpdate()` はリアクティブデータの変更によるコンポーネントのDOM再レンダリング直前に実行されるフックであり、`onUpdated()` はDOM更新完了後に実行されるフックである。`onBeforeMount`/`onMounted` と同様に `createHook` ファクトリと `injectHook` メカニズムを使用する。

**業務上の目的・背景**：コンポーネントの再レンダリング時に、DOM更新前の状態取得（スクロール位置の保存等）や、DOM更新後の後処理（サードパーティライブラリの再初期化等）を行う仕組みを提供する。

**機能の利用シーン**：
- `onBeforeUpdate()`: DOM更新前のスクロール位置・フォーカス状態の保存、アニメーションの準備
- `onUpdated()`: DOM更新後のサードパーティライブラリへの通知、DOM測定値の再計算、Chart.jsやD3.js等の再描画

**主要な処理内容**：
1. `createHook(LifecycleHooks.BEFORE_UPDATE)` / `createHook(LifecycleHooks.UPDATED)` でフック登録関数を生成
2. `injectHook()` でコンポーネントインスタンスのフック配列（`bu` / `u`）にコールバックを追加
3. `onBeforeUpdate` のフックは `componentUpdateFn` 内でDOM更新前に `invokeArrayFns(bu)` として同期実行される
4. `onUpdated` のフックは `componentUpdateFn` 内でDOM更新後に `queuePostRenderEffect(u)` としてキュー実行される
5. 初回マウント時には実行されない（更新サイクルのみ）

**関連システム・外部連携**：レンダラー（`renderer.ts`）の `setupRenderEffect` 関数内の `componentUpdateFn`（更新パス）で呼び出される。

**権限による制御**：権限による制御は存在しない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 該当なし | - | onBeforeUpdate/onUpdatedは汎用フックであり、画面機能マッピングに直接対応する画面はない |

## 機能種別

ライフサイクルフック / 更新処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| hook | () => any | Yes | 更新前/後に実行するコールバック関数 | - |
| target | ComponentInternalInstance \| null | No | フック登録先のコンポーネントインスタンス。デフォルトはcurrentInstance | アクティブなインスタンスがない場合はDEVで警告 |

### 入力データソース

`setup()` 関数内または Composition API 関数内で開発者が直接呼び出す。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | void | 戻り値なし |

### 出力先

コンポーネントインスタンスの内部フック配列（`instance.bu` / `instance.u`）に登録される。

## 処理フロー

### 処理シーケンス

```
1. createHook(LifecycleHooks.BEFORE_UPDATE / UPDATED) で登録関数を生成
2. 登録関数呼び出し時:
   ├─ SSRコンポーネントセットアップ中の場合: 登録をスキップ
   └─ 通常: injectHook() を呼び出し
3. injectHook(type, hook, target):
   ├─ target[type] 配列を取得（なければ初期化）
   ├─ __weh ラッパーを生成
   └─ hooks.push(wrappedHook)
4. レンダラーでの実行（更新パス、instance.isMounted === true 時）:
   ├─ toggleRecurse(instance, false) でエフェクト再帰を無効化
   ├─ bu (onBeforeUpdate): invokeArrayFns(bu) として同期実行
   ├─ toggleRecurse(instance, true) でエフェクト再帰を再有効化
   ├─ renderComponentRoot + patch でDOM更新
   └─ u (onUpdated): queuePostRenderEffect(u, parentSuspense) としてキュー実行
```

### フローチャート

```mermaid
flowchart TD
    A[リアクティブデータ変更] --> B[componentUpdateFn 更新パス]
    B --> C[toggleRecurse false]
    C --> D{bu フックあり?}
    D -->|Yes| E[invokeArrayFns bu 同期実行]
    D -->|No| F[スキップ]
    E --> G[toggleRecurse true]
    F --> G
    G --> H[renderComponentRoot]
    H --> I[patch prevTree, nextTree]
    I --> J{u フックあり?}
    J -->|Yes| K[queuePostRenderEffect u]
    J -->|No| L[スキップ]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-19-01 | 初回マウント時非実行 | onBeforeUpdate/onUpdatedフックは初回マウント時には実行されない | instance.isMountedがtrueの場合のみ実行 |
| BR-19-02 | SSR時のスキップ | SSRコンポーネントセットアップ中はフック登録がスキップされる | isInSSRComponentSetupがtrueの場合 |
| BR-19-03 | onBeforeUpdateの同期実行 | onBeforeUpdateフックはDOM再パッチ前に同期的に実行される | 更新サイクル内 |
| BR-19-04 | onUpdatedの非同期キュー | onUpdatedフックはDOM再パッチ後にqueuePostRenderEffectでキュー実行される | 更新サイクル内 |
| BR-19-05 | エフェクト再帰制御 | onBeforeUpdateフック実行中はコンポーネントエフェクトの再帰が無効化される | toggleRecurseにより制御 |
| BR-19-06 | DOM状態の保証 | onUpdated実行時にはDOMが更新済みであることが保証される | post render effectとして実行 |

### 計算ロジック

特に複雑な計算ロジックは存在しない。

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

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 警告（DEV） | アクティブなコンポーネントインスタンスがない状態での呼び出し | setup()内で呼び出す |
| LifecycleHooks.BEFORE_UPDATE / UPDATED | ランタイムエラー | フック内で発生した例外 | callWithAsyncErrorHandlingで捕捉 |

### リトライ仕様

該当なし。

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

該当なし。

## パフォーマンス要件

- `onUpdated` フックは更新サイクルごとに実行されるため、重い処理を含める場合はパフォーマンスに注意が必要。
- `onBeforeUpdate` 内でリアクティブ値を変更すると無限ループの原因となり得る（toggleRecurseで制御されているが注意が必要）。

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

特になし。

## 備考

- `onBeforeUpdate` のフックはインスタンスの `bu` プロパティに格納される（LifecycleHooks.BEFORE_UPDATE = 'bu'）。
- `onUpdated` のフックはインスタンスの `u` プロパティに格納される（LifecycleHooks.UPDATED = 'u'）。
- `onUpdated` 内での状態変更は再度更新をトリガーするため、無限ループに注意が必要。
- 特定のリアクティブ値の変更を監視する場合は `watch()` / `watchEffect()` を使用することが推奨される。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | enums.ts | `packages/runtime-core/src/enums.ts` | LifecycleHooks列挙型（BEFORE_UPDATE='bu', UPDATED='u'） |

#### Step 2: フック登録メカニズムを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | apiLifecycle.ts | `packages/runtime-core/src/apiLifecycle.ts` | onBeforeUpdate/onUpdatedの定義（87-90行目） |
| 2-2 | apiLifecycle.ts | `packages/runtime-core/src/apiLifecycle.ts` | createHookファクトリ（66-79行目）、injectHook（20-64行目） |

**主要処理フロー**:
- **87-88行目**: `onBeforeUpdate = createHook(LifecycleHooks.BEFORE_UPDATE)`
- **90行目**: `onUpdated = createHook(LifecycleHooks.UPDATED)`

#### Step 3: レンダラーでの実行を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | renderer.ts | `packages/runtime-core/src/renderer.ts` | componentUpdateFn更新パス（1470-1591行目） |

**主要処理フロー**:
- **1471行目**: `let { next, bu, u, parent, vnode } = instance` で更新パスの変数を分解
- **1504行目**: `toggleRecurse(instance, false)` でエフェクト再帰無効化
- **1513-1514行目**: `if (bu) { invokeArrayFns(bu) }` でbeforeUpdateフック同期実行
- **1526行目**: `toggleRecurse(instance, true)` で再帰再有効化
- **1532行目**: `renderComponentRoot(instance)` で新しいサブツリーを生成
- **1542-1552行目**: `patch(prevTree, nextTree, ...)` でDOM更新
- **1564-1565行目**: `if (u) { queuePostRenderEffect(u, parentSuspense) }` でupdatedフックをキュー

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

```
onBeforeUpdate(hook) / onUpdated(hook)
    │
    └─ createHook(lifecycle)
           └─ injectHook(lifecycle, hook, target)

レンダラー componentUpdateFn (更新パス、isMounted === true):
    │
    ├─ toggleRecurse(instance, false)
    ├─ invokeArrayFns(bu)              ← onBeforeUpdateフック実行
    ├─ toggleRecurse(instance, true)
    ├─ renderComponentRoot(instance)   ← 新しいVNodeツリー生成
    ├─ patch(prevTree, nextTree)       ← DOM更新
    └─ queuePostRenderEffect(u)        ← onUpdatedフックをキュー
```

### データフロー図

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

hookコールバック ──────▶ injectHook()              ──▶ instance.bu[] / instance.u[]
                              │
リアクティブ変更   ──────▶ componentUpdateFn (更新)
                              │
                       ├─ invokeArrayFns(bu)   ──▶ フック同期実行 (beforeUpdate)
                       ├─ patch(prevTree, nextTree) ▶ DOM更新
                       └─ queuePostRenderEffect(u) ▶ フック非同期実行 (updated)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| apiLifecycle.ts | `packages/runtime-core/src/apiLifecycle.ts` | ソース | onBeforeUpdate/onUpdatedの定義 |
| enums.ts | `packages/runtime-core/src/enums.ts` | ソース | LifecycleHooks列挙型 |
| renderer.ts | `packages/runtime-core/src/renderer.ts` | ソース | componentUpdateFn内でのフック呼び出し |
| component.ts | `packages/runtime-core/src/component.ts` | ソース | ComponentInternalInstance |
| errorHandling.ts | `packages/runtime-core/src/errorHandling.ts` | ソース | callWithAsyncErrorHandling |
| scheduler.ts | `packages/runtime-core/src/scheduler.ts` | ソース | queuePostRenderEffect |
