# 機能設計書 30-useTemplateRef()

## 概要

本ドキュメントは、Vue 3の`useTemplateRef()`フックの機能設計を記述する。テンプレート内のDOM要素やコンポーネントインスタンスを型安全に参照するためのComposition APIフックであり、TypeScriptとの統合において強力な型推論を提供する。

### 本機能の処理概要

`useTemplateRef()`はテンプレート内の`ref`属性で指定されたDOM要素またはコンポーネントインスタンスへの参照を、型安全なShallowRefとして取得するフックである。コンポーネントインスタンスの`refs`オブジェクトにプロパティディスクリプタを設定し、ref値の変更をリアクティブに追跡する。

**業務上の目的・背景**：テンプレート内のDOM要素やコンポーネントに対してプログラムから操作を行う場合（フォーカス制御、スクロール操作、サイズ計測等）、その参照をsetup()関数内で型安全に取得する必要がある。従来の文字列ref（`ref="myRef"`）では型推論が効かなかったが、`useTemplateRef()`によりTypeScriptでの型安全なDOM参照が実現される。

**機能の利用シーン**：テンプレート内のDOM要素への型安全なアクセス（input要素のフォーカス制御等）、子コンポーネントのパブリックインスタンスへの参照取得、DOM要素のサイズ・位置計測、サードパーティライブラリへのDOM要素の引き渡し。

**主要な処理内容**：
1. `useTemplateRef(key)`でShallowRef（初期値null）を生成
2. コンポーネントインスタンスの`refs`オブジェクトに`key`に対応するプロパティディスクリプタを設定
3. プロパティディスクリプタのget/setにより、ShallowRefの値と`refs[key]`を双方向に同期
4. 開発モードではreadonly()でラップし、外部からの直接書き込みを防止
5. `knownTemplateRefs`WeakSetに登録し、setRef処理時の特別扱いに使用

**関連システム・外部連携**：`setRef`関数（レンダリング時のref設定処理）、`rendererTemplateRef.ts`（テンプレートref管理）、`@vue/reactivity`パッケージ（shallowRef、readonly）と連携する。

**権限による制御**：権限による制御は存在しない。Composition APIのsetup()関数内でのみ使用可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 画面機能マッピングに直接の関連画面なし。テンプレートrefを使用する任意のコンポーネントで使用される |

## 機能種別

DOM参照取得 / テンプレート連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| key | string (Keys extends string) | Yes | テンプレート内のref属性に対応するキー文字列 | 開発モードで同一キーの重複チェック |

### 入力データソース

Composition APIのsetup()関数内からのプログラム呼び出し。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| ref | TemplateRef\<T\> (= Readonly\<ShallowRef\<T \| null\>\>) | テンプレート要素への読み取り専用リアクティブ参照。初期値はnull |

### 出力先

呼び出し元への戻り値。

## 処理フロー

### 処理シーケンス

```
1. useTemplateRef(key) 呼び出し
   └─ getCurrentInstance() でコンポーネントインスタンスを取得
2. shallowRef(null) でリアクティブ参照を生成
3. インスタンスが存在する場合
   ├─ refs === EMPTY_OBJ の場合：refs = {} に初期化
   ├─ 開発モード：同一keyのプロパティディスクリプタ重複チェック
   └─ Object.defineProperty(refs, key, { get: () => r.value, set: val => r.value = val })
4. 開発モード：readonly(r) でラップ、knownTemplateRefs.add(ret)
5. 本番モード：r をそのまま返却
6. テンプレートレンダリング時
   └─ setRef() により refs[key] に値が設定され、プロパティディスクリプタ経由でr.valueが更新
```

### フローチャート

```mermaid
flowchart TD
    A[useTemplateRef key] --> B[getCurrentInstance]
    B --> C[shallowRef null]
    C --> D{instance存在?}
    D -->|Yes| E{refs === EMPTY_OBJ?}
    D -->|No| F[開発警告]
    E -->|Yes| G[refs = 空オブジェクト]
    E -->|No| H[既存refsを使用]
    G --> I[Object.defineProperty refs key get/set]
    H --> I
    I --> J{__DEV__?}
    J -->|Yes| K[readonly r]
    J -->|No| L[r をそのまま返却]
    K --> M[knownTemplateRefs.add]
    M --> N[返却]
    L --> N
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-30-01 | ShallowRef使用 | 通常のrefではなくshallowRefを使用し、参照先オブジェクト自体は深いリアクティブ変換を行わない | 常時 |
| BR-30-02 | プロパティディスクリプタ | Object.definePropertyでget/setを定義し、refs[key]への設定がShallowRefの値更新に自動的に反映される | インスタンス存在時 |
| BR-30-03 | 読み取り専用ラップ | 開発モードではreadonly()でラップし、ユーザーコードからの直接書き込みを防止する | 開発モード |
| BR-30-04 | knownTemplateRefs登録 | 開発モードでknownTemplateRefs WeakSetに登録し、setRef処理時にuseTemplateRefで作成されたrefを識別する | 開発モード |
| BR-30-05 | 重複チェック | 同一keyに対して既にconfigurable: falseのプロパティが存在する場合は警告を出力する | 開発モード |

### 計算ロジック

該当なし。

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

### 操作別データベース影響一覧

該当なし（クライアントサイドフレームワーク機能）。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 開発警告 | setup()関数外でuseTemplateRefを呼び出した場合 | `useTemplateRef() is called when there is no active component instance`警告 |
| - | 開発警告 | 同一keyに対して重複してuseTemplateRefを呼び出した場合 | `useTemplateRef('key') already exists.`警告 |

### リトライ仕様

リトライ機構は存在しない。

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

該当なし。

## パフォーマンス要件

- ShallowRefを使用するため、DOM要素/コンポーネントインスタンスに対する深いリアクティブプロキシ化を回避
- Object.definePropertyによるget/setインターセプトは微小なオーバーヘッド

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

- DOM要素への直接アクセスを提供するため、DOM操作に伴うXSSリスク（innerHTML設定等）に注意が必要

## 備考

- `useTemplateRef`はVue 3.5で追加された比較的新しいAPI
- 従来の`ref("myRef")`による文字列ref参照と比較して、TypeScriptの型推論が大幅に改善される
- `TemplateRef<T>`型は`Readonly<ShallowRef<T | null>>`のエイリアス
- `knownTemplateRefs`はWeakSetであり、GCに影響しない

---

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

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

### 推奨読解順序

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

TemplateRef型とknownTemplateRefsを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | useTemplateRef.ts | `packages/runtime-core/src/helpers/useTemplateRef.ts` | `TemplateRef<T>`型（8行目）。`Readonly<ShallowRef<T \| null>>`型エイリアス |
| 1-2 | useTemplateRef.ts | `packages/runtime-core/src/helpers/useTemplateRef.ts` | `knownTemplateRefs`（6行目）。`WeakSet<ShallowRef>`型。setRef処理時の識別に使用 |

**読解のコツ**: `TemplateRef<T>`は読み取り専用のShallowRefであり、ユーザーコードから`.value`への書き込みは型エラーとなる。ただし内部的にはsetRef処理から書き込みが可能。

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

useTemplateRef関数の実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | useTemplateRef.ts | `packages/runtime-core/src/helpers/useTemplateRef.ts` | useTemplateRef関数（10-42行目） |

**主要処理フロー**:
1. **13行目**: `getCurrentInstance()`でインスタンス取得
2. **14行目**: `shallowRef(null)`でリアクティブ参照を生成
3. **15-16行目**: インスタンス存在確認、refsの初期化
4. **25-29行目**: `Object.defineProperty(refs, key, { enumerable: true, get: () => r.value, set: val => (r.value = val) })`
5. **37行目**: 開発モードで`readonly(r)`でラップ
6. **39行目**: 開発モードで`knownTemplateRefs.add(ret)`

#### Step 3: setRefとの連携を理解する

テンプレートレンダリング時にsetRefがuseTemplateRefの参照を更新する仕組みを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | rendererTemplateRef.ts | `packages/runtime-core/src/rendererTemplateRef.ts` | setRef関数（31-196行目）。refs[key]への設定がプロパティディスクリプタ経由でShallowRefを更新 |
| 3-2 | rendererTemplateRef.ts | `packages/runtime-core/src/rendererTemplateRef.ts` | knownTemplateRefsの使用（25行目import、97-98行目、105行目）。useTemplateRefで作成されたrefは直接書き込みをスキップ |

**主要処理フロー**:
- **25行目**: `knownTemplateRefs`をインポート
- **97-98行目**: `knownTemplateRefs.has(rawSetupState[key])`の場合、`canSetSetupRef`がfalseを返す（useTemplateRefで作成されたrefへの直接書き込みを防止）
- **105行目**: `canSetRef`関数内で`knownTemplateRefs.has(ref)`を確認
- **164行目**: `refs[ref] = value`による設定がObject.definePropertyのsetを発火し、ShallowRefのvalueが更新される

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

```
useTemplateRef(key)
    │
    ├─ getCurrentInstance()
    │
    ├─ shallowRef(null) → r
    │
    ├─ Object.defineProperty(refs, key, {get, set})
    │      ├─ get: () => r.value
    │      └─ set: val => r.value = val
    │
    ├─ [__DEV__] readonly(r) → ret
    │
    └─ [__DEV__] knownTemplateRefs.add(ret)

[テンプレートレンダリング時]
setRef(rawRef, ...)
    │
    └─ refs[key] = value
           └─ プロパティディスクリプタのset発火
                  └─ r.value = value  [ShallowRef更新]
```

### データフロー図

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

key (string)     ───▶ useTemplateRef()              ───▶ TemplateRef<T>
                         │                                  (Readonly<ShallowRef>)
                         ├─ shallowRef(null)
                         ├─ defineProperty(refs, key)
                         └─ readonly (DEV)

[テンプレートレンダリング時]
DOM Element /    ───▶ setRef()                      ───▶ refs[key] = element
Component              │                                  │
                       └─ defineProperty set             ▼
                                                   r.value = element
                                                          │
                                                          ▼
                                                   TemplateRef.value更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| useTemplateRef.ts | `packages/runtime-core/src/helpers/useTemplateRef.ts` | ソース | useTemplateRef関数、TemplateRef型、knownTemplateRefs |
| rendererTemplateRef.ts | `packages/runtime-core/src/rendererTemplateRef.ts` | ソース | setRef関数（レンダリング時のref設定処理）、knownTemplateRefsの参照 |
| index.ts | `packages/runtime-core/src/index.ts` | ソース | useTemplateRefのre-export |
