# 画面設計書 8-Page A

## 概要

本ドキュメントは、webpackサンプルプロジェクト「hybrid-routing」におけるPage Aの画面設計書である。hybrid-routingは、静的エントリポイントと動的インポートによるハイブリッドなルーティングパターンを示すサンプルであり、Page Aはその一方のページである。

### 本画面の処理概要

Page Aは、hybrid-routingサンプルにおける2つのページの1つである。aEntry.jsによる静的読み込みと、router.jsの`window.onLinkToPage`関数による動的インポート（`import()`)の2つの経路でアクセスされる。Page Aの実体は`aPage.js`であり、文字列 "This is page A." を返す関数をエクスポートする。

**業務上の目的・背景**：webpackにおけるハイブリッドルーティングパターン（静的エントリ + 動的import）を実証する。HTMLページ（pageA.html）は静的エントリとして初期表示用にPage Aのコードを含み、同時にrouterモジュールを含むことで他ページへの動的遷移も可能にする。これにより、初期表示の高速化とSPA的な遷移を両立する設計を示す。

**画面へのアクセス方法**：
1. pageA.htmlを直接アクセスする（静的エントリ経路）：aEntry.jsが自動的にaPage.jsを読み込み、render関数でコンソール出力
2. 他のページからrouter.jsの`window.onLinkToPage("a")`を呼び出す（動的import経路）：import()でaPage.jsを遅延読み込み

**主要な操作・処理内容**：
1. 静的経路：aEntry.jsがrender関数とaPage.jsを同期的にrequireし、render(aPage)を実行
2. 動的経路：router.jsがimport(`./${name}Page`)でaPage.jsを非同期に読み込み、render(page.default)を実行
3. render関数がpage()を呼び出し、戻り値 "This is page A." をconsole.logで出力
4. SplitChunksPluginにより、router.jsはPage AとPage Bの共有チャンク（pageA~pageB.chunk.js）に分割される

**画面遷移**：
- router.jsの`window.onLinkToPage("b")`を呼び出すことでPage Bへ遷移可能
- Page Bからも`window.onLinkToPage("a")`でPage Aに遷移可能
- Page AとPage Bは共有チャンク（pageA~pageB.chunk.js）を介して相互遷移する

**権限による表示制御**：該当なし。本サンプルには認証・認可機能は実装されていない。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 13 | 動的インポート | 主機能 | router.jsのimport()による遅延読み込みターゲット。ページ遷移時に動的にロードされる |
| 4 | エントリポイント管理 | 主機能 | aEntryおよびrouterと共にエントリポイントとして登録 |
| 12 | SplitChunksPlugin | 補助機能 | routerモジュール等の共通コードをPageBと共有チャンクに分割 |
| 45 | JSONPチャンクローディング | 補助機能 | ブラウザ環境での動的チャンク読み込みランタイム |
| 10 | チャンクグラフ構築 | 補助機能 | 動的チャンクを含むチャンクグラフの構築 |
| 11 | チャンクグループ管理 | 遷移先機能 | ページ遷移時のチャンク依存関係管理 |

## 画面種別

ハイブリッドルーティングページ（静的エントリ + 動的インポート対応）

## URL/ルーティング

- HTMLファイル: `pageA.html`
- エントリポイント名: `pageA`（webpack.config.jsのentry設定）
- エントリ構成: `["./aEntry", "./router"]`
- 出力ファイル: `pageA.bundle.js`
- 動的チャンク: `aPage.chunk.js`
- 共有チャンク: `pageA~pageB.chunk.js`

## 入出力項目

| 項目名 | 種別 | 型 | 説明 |
|--------|------|-----|------|
| aPage関数 | 出力（export） | Function | "This is page A."を返す関数。module.exportsで公開 |
| name | 入力（router経由） | string | "a" - 動的import時のページ識別子 |

## 表示項目

本画面はHTMLページ（pageA.html）として提供されるが、視覚的なUI要素は持たない。コンソールへのテキスト出力がメインの表示である。

| 出力先 | 内容 | トリガー |
|--------|------|---------|
| console | "This is page A." | aEntry.js読み込み時（静的経路）またはrouter.js遷移時（動的経路） |

## イベント仕様

### 1-静的ページ読み込み（pageA.html）

pageA.htmlがブラウザで読み込まれる際の処理フロー。

1. 共有チャンク（pageA~pageB.chunk.js）が非同期読み込みされる
2. 動的チャンク（aPage.chunk.js）が非同期読み込みされる
3. エントリバンドル（pageA.bundle.js）が読み込まれる
4. aEntry.jsが実行：`render(require("./aPage"))` が呼ばれる
5. render関数が`aPage()`を呼び出し、"This is page A."をconsole.logで出力
6. router.jsが実行：`window.onLinkToPage`関数がグローバルに登録される

### 2-動的ページ遷移（onLinkToPage）

他のページからrouter.jsの`window.onLinkToPage("a")`が呼ばれた際の処理フロー。

1. router.jsが`import(/* webpackChunkName: "[request]" */\`./${name}Page\`)`を実行
2. webpackランタイムがaPage.chunk.jsを動的にフェッチ・評価
3. import()のPromiseが解決され、`page.default`（aPage関数）が取得される
4. render関数が`aPage()`を呼び出し、"This is page A."をconsole.logで出力

## データベース更新仕様

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

該当なし。本画面はデータベースを使用しない。

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 出力条件 |
|-------------|------|---------------|---------|
| M-01 | 情報 | "This is page A." | ページ読み込み時（静的・動的どちらの経路でも） |

## 例外処理

| 例外条件 | 動作 |
|---------|------|
| 動的チャンクの読み込み失敗 | import()のPromiseがrejectされる。router.jsにはエラーハンドリングがないため、未処理のPromise rejectionとなる |
| 存在しないページ名の指定 | import()が実行時エラーをスロー（webpackのContextモジュールでマッチしない場合） |

## 備考

- router.jsのimport()には`webpackChunkName: "[request]"`マジックコメントが使用されており、動的チャンク名がリクエストパスに基づいて決定される
- pageA.htmlでは3つのscriptタグが全てasync属性付きで読み込まれるため、読み込み順序はブラウザに委ねられる
- splitChunksのminSizeは0に設定されており、小さなモジュールでも共有チャンクに分割される
- chunkIdsは"named"に設定されており、ビルドモード間でファイル名の一貫性を保つ

---

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

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

### 推奨読解順序

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

まず、ページモジュールとrender関数のインターフェースを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | aPage.js | `examples/hybrid-routing/aPage.js` | ページモジュール。"This is page A."を返す関数をmodule.exportsでエクスポート（行1〜3） |
| 1-2 | render.js | `examples/hybrid-routing/render.js` | レンダリング関数。page()を呼び出しconsole.logで出力（行1〜3） |

**読解のコツ**: aPage.jsはCommonJS形式（module.exports）、router.jsはESModule形式（import()）を使用しており、webpackがモジュール形式の違いを吸収している点に注目する。

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

Page Aのエントリポイントは2つのモジュール（aEntry.js, router.js）で構成される。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | aEntry.js | `examples/hybrid-routing/aEntry.js` | 静的エントリ。render関数でaPage.jsを即座に実行（行1〜3） |
| 2-2 | router.js | `examples/hybrid-routing/router.js` | 動的ルーター。window.onLinkToPage関数を定義し、import()でページを遅延読み込み（行1〜18） |

**主要処理フロー（aEntry.js）**:
1. **行2**: `var render = require("./render")` - render関数を読み込み
2. **行3**: `render(require("./aPage"))` - aPage.jsを同期読み込みしrender実行

**主要処理フロー（router.js）**:
1. **行1**: `var render = require("./render")` - render関数を読み込み
2. **行5**: `window.onLinkToPage = function onLinkToPage(name)` - グローバル関数を定義
3. **行15**: `import(/* webpackChunkName: "[request]" */\`./${name}Page\`)` - 動的import実行
4. **行15-17**: `.then(page => { render(page.default); })` - Promise解決後にrender実行

#### Step 3: webpack設定とHTML構成を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | webpack.config.js | `examples/hybrid-routing/webpack.config.js` | エントリ定義（行11: ["./aEntry", "./router"]）、出力設定、splitChunks設定 |
| 3-2 | pageA.html | `examples/hybrid-routing/pageA.html` | HTMLテンプレート。3つのasyncスクリプトの読み込み順序（行4〜6） |

**主要処理フロー（webpack.config.js）**:
- **行11**: `pageA: ["./aEntry", "./router"]` - 2つのモジュールでエントリを構成
- **行16**: `filename: "[name].bundle.js"` - エントリバンドルのファイル名パターン
- **行17**: `chunkFilename: "[name].chunk.js"` - 動的チャンクのファイル名パターン
- **行23**: `chunks: "all"` - 初期チャンクも含めた分割
- **行24**: `minSize: 0` - サイズ制限なしで分割

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

```
pageA.html
    |
    +-- [async] pageA~pageB.chunk.js (共有チャンク: router.js)
    +-- [async] aPage.chunk.js (動的チャンク: aPage.js)
    +-- [async] pageA.bundle.js (エントリバンドル)
            |
            +-- aEntry.js (静的エントリ)
            |       +-- render.js
            |       +-- aPage.js --> "This is page A."
            |
            +-- router.js (動的ルーター)
                    +-- render.js
                    +-- window.onLinkToPage(name)
                            +-- import(`./${name}Page`) --> aPage.js or bPage.js
                                    +-- render(page.default)
```

### データフロー図

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

--- 静的経路 ---
pageA.html ──────────> aEntry.js ──> require("./aPage") ──> render() ──> console.log("This is page A.")

--- 動的経路 ---
onLinkToPage("a") ──> router.js ──> import("./aPage") ──> render(page.default) ──> console.log("This is page A.")
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| aPage.js | `examples/hybrid-routing/aPage.js` | ソース | Page Aのページモジュール |
| aEntry.js | `examples/hybrid-routing/aEntry.js` | ソース | Page Aの静的エントリポイント |
| router.js | `examples/hybrid-routing/router.js` | ソース | 動的ルーター。import()でページを遅延読み込み |
| render.js | `examples/hybrid-routing/render.js` | ソース | レンダリング関数。page()の結果をconsole.log出力 |
| pageA.html | `examples/hybrid-routing/pageA.html` | テンプレート | Page A用のHTMLテンプレート |
| webpack.config.js | `examples/hybrid-routing/webpack.config.js` | 設定 | webpackビルド設定 |
| bPage.js | `examples/hybrid-routing/bPage.js` | ソース | Page Bのページモジュール（遷移先） |
| pageB.html | `examples/hybrid-routing/pageB.html` | テンプレート | Page B用のHTMLテンプレート（遷移先） |
