# 機能設計書 51-next/link（Link）

## 概要

本ドキュメントは、Next.jsの`next/link`（`<Link>`コンポーネント）の機能設計を記述する。`<Link>`コンポーネントはクライアントサイドナビゲーションの主要なインターフェースであり、HTML標準の`<a>`要素を拡張してプリフェッチとソフトナビゲーションの機能を提供する。

### 本機能の処理概要

**業務上の目的・背景**：Webアプリケーションにおいて、ページ間遷移はユーザー体験の根幹をなす操作である。従来のフルページリロードによるナビゲーションはレスポンスが遅く、ユーザー体験を大きく損なう。`<Link>`コンポーネントは、クライアントサイドルーティングによりJavaScriptベースのソフトナビゲーションを実現し、SPA（Single Page Application）に近い高速なページ遷移を提供する。同時にSEOやアクセシビリティを損なわないよう、標準の`<a>`要素としてレンダリングされる。

**機能の利用シーン**：アプリケーション内のあらゆるページ間リンクで使用される。ナビゲーションメニュー、記事一覧からの詳細ページへのリンク、パンくずリスト、フッターリンクなど、ユーザーがクリックしてページ遷移する場面のほぼ全てで利用される。

**主要な処理内容**：
1. hrefの解決とフォーマット処理（URLオブジェクト/文字列の正規化）
2. IntersectionObserverによるビューポート内検出とプリフェッチの自動実行
3. クリックイベントのハンドリングとクライアントサイドナビゲーションの実行
4. マウスホバー/タッチ開始時の優先度付きプリフェッチ
5. Pages Router / App Router両方のルーターとの統合
6. ロケール管理とbasePath付加
7. legacyBehavior（後方互換）モードのサポート

**関連システム・外部連携**：Pages Router（`NextRouter`）およびApp Router（`AppRouterInstance`）と連携してナビゲーションを実行する。ロケールドメイン設定、basePath設定とも統合される。

**権限による制御**：特にロールや権限による実行制御は行われない。ただし、`onNavigate`コールバックにより、アプリケーションレベルでナビゲーションの許可/拒否制御が可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 該当なし | - | Linkコンポーネントは特定の画面に紐づかず、全画面で利用可能な汎用ナビゲーションコンポーネント |

## 機能種別

ナビゲーション / UIコンポーネント

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| href | string \| UrlObject | Yes | ナビゲーション先のパスまたはURL | null/undefinedでない文字列またはオブジェクトであること |
| as | string \| UrlObject | No | ブラウザURLバーに表示されるデコレーターパス | 文字列またはオブジェクト |
| replace | boolean | No | historyにpushではなくreplaceするか | boolean型 |
| scroll | boolean | No | スクロール動作をオーバーライドするか（デフォルト: true） | boolean型 |
| shallow | boolean | No | データフェッチなしでパスを更新するか（Pages Router専用） | boolean型 |
| passHref | boolean | No | hrefを子要素に強制的に渡すか | boolean型 |
| prefetch | boolean \| 'auto' \| null | No | プリフェッチの有効/無効制御 | boolean, 'auto', null |
| locale | string \| false | No | ロケール設定のオーバーライド（Pages Router専用） | 文字列またはfalse |
| legacyBehavior | boolean | No | レガシーリンク動作の有効化（非推奨） | boolean型 |
| onNavigate | function | No | ナビゲーション時のイベントハンドラ | 関数型 |
| onClick | function | No | クリック時のイベントハンドラ | 関数型 |
| onMouseEnter | function | No | マウスホバー時のイベントハンドラ | 関数型 |
| onTouchStart | function | No | タッチ開始時のイベントハンドラ | 関数型 |

### 入力データソース

- コンポーネントProps経由でのユーザー指定値
- RouterContext（Pages Router）またはAppRouterContext（App Router）からのルーター状態

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| HTMLアンカー要素 | React.ReactElement | `<a>`要素としてレンダリングされるReact要素 |
| プリフェッチ実行 | void | ルーターのprefetchメソッド呼び出しによるバックグラウンドデータ取得 |
| ナビゲーション実行 | void | ルーターのpush/replaceメソッド呼び出しによるクライアントサイド遷移 |

### 出力先

- DOMへの`<a>`要素レンダリング
- ルーターへのプリフェッチ/ナビゲーション指示

## 処理フロー

### 処理シーケンス

```
1. コンポーネントマウント・Props受け取り
   └─ href, as, prefetch, replace, scroll等のPropsを分解
2. hrefの解決
   └─ resolveHrefによりURLを正規化、asの解決
3. Propsバリデーション（開発モードのみ）
   └─ 型チェック、必須チェック、レガシー動作の警告
4. IntersectionObserver設定
   └─ useIntersectionフックでビューポート内検出（rootMargin: 200px）
5. プリフェッチ（プロダクション環境のみ）
   └─ ビューポート内に表示された場合、prefetchEnabledならprefetch実行
6. イベントハンドラの設定
   ├─ onClick: linkClicked関数でナビゲーション実行
   ├─ onMouseEnter: 優先度付きプリフェッチ実行
   └─ onTouchStart: 優先度付きプリフェッチ実行
7. レンダリング
   ├─ legacyBehavior: React.cloneElementで子要素にPropsを注入
   └─ 通常: <a>要素としてレンダリング
```

### フローチャート

```mermaid
flowchart TD
    A[Link コンポーネントマウント] --> B[href/as の解決]
    B --> C{legacyBehavior?}
    C -->|Yes| D[子要素のバリデーション]
    C -->|No| E[<a>子要素チェック]
    D --> F[IntersectionObserver 設定]
    E --> F
    F --> G{ビューポート内 & prefetchEnabled?}
    G -->|Yes| H[プリフェッチ実行]
    G -->|No| I[プリフェッチスキップ]
    H --> J[イベントハンドラ設定]
    I --> J
    J --> K{ユーザーアクション}
    K -->|クリック| L{修飾キー押下?}
    L -->|Yes| M[ブラウザデフォルト動作]
    L -->|No| N{ローカルURL?}
    N -->|No| O[外部URL遷移]
    N -->|Yes| P[onNavigate呼び出し]
    P --> Q{デフォルト防止?}
    Q -->|Yes| R[ナビゲーションキャンセル]
    Q -->|No| S[ルーター.push/replace実行]
    K -->|ホバー/タッチ| T[優先度付きプリフェッチ]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-51-01 | プリフェッチ重複排除 | 同一URLへのプリフェッチはprefetched Setで重複排除される | bypassPrefetchedCheckがfalseの場合 |
| BR-51-02 | 開発モードプリフェッチ無効 | 開発モードではビューポート内プリフェッチは無効（ホバー時のみ） | NODE_ENV !== 'production' |
| BR-51-03 | 修飾キー無視 | Ctrl/Meta/Shift/Alt押下時はブラウザデフォルト動作に委ねる | クリックイベント時 |
| BR-51-04 | 外部URL無視 | ローカルURLでない場合はクライアントサイドナビゲーションを実行しない | isLocalURLがfalseの場合 |
| BR-51-05 | ロケール付加 | Pages Routerでは現在のロケールまたは指定ロケールをパスに付加する | Pages Router使用時 |

### 計算ロジック

- プリフェッチキー: `href + '%' + as + '%' + locale` で構成される文字列キーにより重複判定
- href解決: `resolveHref`関数により、UrlObjectの正規化、動的ルートのパラメータ補間を実行

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

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

該当なし（クライアントサイドコンポーネントのため、直接的なデータベース操作は行わない）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | PropTypeエラー | hrefがnull/undefinedまたは不正な型 | 開発モードでError throw |
| - | 子要素エラー | legacyBehavior時に子要素が0個または複数 | 開発モードでError throw |
| - | 無効なリンク構造 | 非legacyBehavior時に`<a>`要素が子要素 | 開発モードでError throw |
| - | プリフェッチエラー | プリフェッチ中のネットワークエラー | プロダクション環境ではcatchして無視、開発モードではrethrow |

### リトライ仕様

プリフェッチのリトライは行われない。エラー時はcatchして静かに失敗する。

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

該当なし

## パフォーマンス要件

- IntersectionObserverのrootMarginは200pxに設定され、ビューポート到達前にプリフェッチを開始
- prefetched Setにより同一URLの重複プリフェッチを防止
- ホバー/タッチ時は`priority: true`で優先プリフェッチを実行

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

- `isLocalURL`チェックにより外部URLへのクライアントサイドナビゲーションを防止
- XSS対策として、hrefは`resolveHref`で正規化される

## 備考

- `useLinkStatus`フックが同ファイルでエクスポートされており、Linkのpending状態を取得可能（App Router向け）
- `legacyBehavior`は非推奨であり、将来のバージョンで削除予定

---

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

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

### 推奨読解順序

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

まず、Linkコンポーネントが扱う型定義と設定を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | link.tsx | `packages/next/src/client/link.tsx` | **33-116行目**: `InternalLinkProps`型定義 - href, as, replace, scroll等の全Propsの型と説明 |
| 1-2 | link.tsx | `packages/next/src/client/link.tsx` | **131-139行目**: `prefetched` Set, `PrefetchOptions`型 - プリフェッチ重複排除の仕組み |

**読解のコツ**: `LinkProps`はTypeScriptのジェネリクス型`RouteInferType`を持つinterfaceとして公開されており、typedRoutesとの統合を想定している。

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

Linkコンポーネント本体のforwardRefで包まれた関数コンポーネント。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | link.tsx | `packages/next/src/client/link.tsx` | **295-698行目**: `Link`コンポーネント本体 - forwardRefで定義 |

**主要処理フロー**:
1. **299-315行目**: Propsの分解代入
2. **326行目**: `React.useContext(RouterContext)`でPages Routerを取得
3. **453-468行目**: `useMemo`でhref/asを解決
4. **519-534行目**: `useIntersection`フックでビューポート監視
5. **539-556行目**: `useEffect`でプリフェッチを実行
6. **558-657行目**: childProps（onClick, onMouseEnter, onTouchStart）を構築
7. **680-696行目**: レンダリング分岐（legacy vs 通常）

#### Step 3: プリフェッチ処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | link.tsx | `packages/next/src/client/link.tsx` | **141-188行目**: `prefetch`関数 - ローカルURL判定、重複排除、router.prefetch呼び出し |
| 3-2 | use-intersection.tsx | `packages/next/src/client/use-intersection.tsx` | **96-137行目**: `useIntersection`フック - IntersectionObserverによるビューポート検出 |

#### Step 4: クリックハンドリングを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | link.tsx | `packages/next/src/client/link.tsx` | **190-201行目**: `isModifiedEvent` - 修飾キー検出 |
| 4-2 | link.tsx | `packages/next/src/client/link.tsx` | **203-272行目**: `linkClicked` - ナビゲーション実行ロジック。Pages Router/App Routerの分岐 |

**主要処理フロー**:
- **219-225行目**: 修飾キー押下やdownload属性がある場合はブラウザデフォルト動作
- **227-237行目**: 非ローカルURL時の処理（replaceの場合はlocation.replace）
- **241-268行目**: `navigate`関数 - onNavigateコールバック、ルーター分岐（beforePopState有無で判定）

#### Step 5: href解決処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | resolve-href.ts | `packages/next/src/client/resolve-href.ts` | **18-138行目**: `resolveHref`関数 - URL正規化、動的ルート補間 |

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

```
Link (forwardRef コンポーネント)
    |
    +-- resolveHref() ............... href/as の正規化
    |       +-- formatWithValidation()
    |       +-- normalizePathTrailingSlash()
    |       +-- interpolateAs()
    |
    +-- useIntersection() ........... ビューポート検出
    |       +-- createObserver() ... IntersectionObserver 生成
    |       +-- observe() .......... 要素の監視開始
    |
    +-- prefetch() .................. プリフェッチ実行
    |       +-- isLocalURL()
    |       +-- router.prefetch()
    |
    +-- linkClicked() ............... クリック時ナビゲーション
    |       +-- isModifiedEvent() .. 修飾キー判定
    |       +-- isLocalURL()
    |       +-- onNavigate() ...... ユーザーコールバック
    |       +-- router.push() / router.replace()
    |
    +-- useMergedRef() .............. ref の合成
    +-- addBasePath() ............... basePath の付加
    +-- addLocale() ................. ロケールの付加
    +-- getDomainLocale() ........... ドメインロケール取得
```

### データフロー図

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

Props (href, as, ...) ---> resolveHref() ----------------> 正規化されたhref/as
                           |
RouterContext -----------> コンポーネント本体 ------------> <a> 要素 (DOM)
                           |
IntersectionObserver ----> isVisible判定 ----------------> router.prefetch()
                           |
ユーザー操作 -----------> linkClicked() -----------------> router.push/replace()
(click/hover/touch)        |
                           +-> onNavigate callback ------> ナビゲーション許可/拒否
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| link.tsx | `packages/next/src/client/link.tsx` | ソース | Linkコンポーネント本体（714行） |
| resolve-href.ts | `packages/next/src/client/resolve-href.ts` | ソース | href解決ロジック |
| use-intersection.tsx | `packages/next/src/client/use-intersection.tsx` | ソース | IntersectionObserverフック |
| use-merged-ref.ts | `packages/next/src/client/use-merged-ref.ts` | ソース | ref合成ユーティリティ |
| add-base-path.ts | `packages/next/src/client/add-base-path.ts` | ソース | basePath付加 |
| add-locale.ts | `packages/next/src/client/add-locale.ts` | ソース | ロケール付加 |
| get-domain-locale.ts | `packages/next/src/client/get-domain-locale.ts` | ソース | ドメインロケール取得 |
| is-local-url.ts | `packages/next/src/shared/lib/router/utils/is-local-url.ts` | ソース | ローカルURL判定 |
| format-url.ts | `packages/next/src/shared/lib/router/utils/format-url.ts` | ソース | URLフォーマット |
| router-context.shared-runtime.ts | `packages/next/src/shared/lib/router-context.shared-runtime.ts` | ソース | Pages RouterのReact Context |
| app-router-context.shared-runtime.ts | `packages/next/src/shared/lib/app-router-context.shared-runtime.ts` | ソース | App RouterのReact Context |
