# 【Treeman】React 開發

# 【NextJS】相關

# 【Next.js】建立新專案

```shell
# npx create-next-app 建立新專案
$npx create-next-app
Need to install the following packages:
create-next-app@15.4.3
Ok to proceed? (y) y

✔ What is your project named? … demo-next
✔ Would you like to use TypeScript? … No / Yes #Yes
✔ Would you like to use ESLint? … No / Yes #Yes
✔ Would you like to use Tailwind CSS? … No / Yes #Yes
✔ Would you like your code inside a `src/` directory? … No / Yes #Yes
✔ Would you like to use App Router? (recommended) … No / Yes #Yes
✔ Would you like to use Turbopack for `next dev`? … No / Yes #No
✔ Would you like to customize the import alias (`@/*` by default)? … No / Yes #Yes
✔ What import alias would you like configured? … @/*
Creating a new Next.js app in /Users/srou/Documents/vscode_workspace/demo/nextjs-demo/demo-next.

Using npm.

Initializing project with template: app-tw 


Installing dependencies:
- react
- react-dom
- next

Installing devDependencies:
- typescript
- @types/node
- @types/react
- @types/react-dom
- @tailwindcss/postcss
- tailwindcss


added 59 packages, and audited 60 packages in 20s

11 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
Initialized a git repository.

Success! Created demo-next at /Users/srou/Documents/vscode_workspace/demo/nextjs-demo/demo-next
```

```shell
# 啟動專案 npm run dev
$npm run dev

> demo-next@0.1.0 dev
> next dev

 ⚠ Warning: Found multiple lockfiles. Selecting /Users/srou/Documents/vscode_workspace/yarn.lock.
   Consider removing the lockfiles at:
   * /Users/srou/Documents/vscode_workspace/demo/demo-next/package-lock.json

   ▲ Next.js 15.4.3
   - Local:        http://localhost:3000
   - Network:      http://192.168.1.1:3000 

 ✓ Starting...
 ✓ Ready in 2.1s
```

當你使用 `npx create-next-app` 建立 Next.js 專案時，CLI 會引導你一步一步選擇要開啟哪些功能。以下是每一個步驟的說明，以及如果最後選擇 **不設定 import alias（例如 `@/*`）**，日後如何補上設定。

---

## 🧱 建立 Next.js 專案步驟說明

### 1. `What is your project named?`

> 輸入專案資料夾名稱，例如 `demo-next`。

### 2. `Would you like to use TypeScript?`

> 是否使用 TypeScript（推薦選 Yes，提供型別安全）。

- 選 Yes → 自動生成 `tsconfig.json`
- 選 No → 使用 JavaScript + `jsconfig.json`

---

### 3. `Would you like to use ESLint?`

> 是否啟用 [ESLint](https://eslint.org/) 做程式碼檢查。

- 選 Yes → 建立 `.eslintrc.json` 等設定，避免潛在錯誤。

---

### 4. `Would you like to use Tailwind CSS?`

> 是否加入 [Tailwind CSS](https://tailwindcss.com/) 做 UI 樣式設計。

---

### 5. `Would you like your code inside a src/ directory?`

> 是否將程式碼放入 `src/` 子資料夾中。

- 選 Yes → 專案結構更清晰，建議開啟。

---

### 6. `Would you like to use App Router? (recommended)`

> 啟用 Next.js 13+ 的新 **App Router** 架構（基於 `app/` 資料夾）。

- 選 Yes → 使用新路由系統，支援 Server Components 等現代功能。

---

### 7. `Would you like to use Turbopack for next dev?`

> 是否使用新一代 Turbopack 開發模式（取代 Webpack 開發時編譯器）。

- 只會影響 `npm run dev` 時的速度
- 生產環境仍使用 Webpack

---

### 8. `Would you like to customize the import alias (@/* by default)?`

> 是否要自訂 **import 路徑別名 alias**？

#### ✅ 選 Yes：

- CLI 會再問你輸入別名（例如 `@/*`），
- 會幫你自動建立 `paths` 設定（在 `tsconfig.json` 或 `jsconfig.json`）

#### ❌ 選 No：

- 不會設定別名，之後你手動寫 `../../components/...` 等麻煩路徑。

---

## 🔧 若選擇 "No"，之後如何補上 import alias？

### ▶ 1. 找出設定檔

如果你是：

- 用 TypeScript → 修改 `tsconfig.json`
- 用 JavaScript → 修改 `jsconfig.json`

### ▶ 2. 修改範例如下（代表 `@` 指向 `src` 資料夾）：

```jsonc
{
  "compilerOptions": {
    "baseUrl": ".",           // 專案根目錄
    "paths": {
      "@/*": ["src/*"]
    }
  }
}

```

### ▶ 3. 如果你沒使用 `src/`，就寫：

```jsonc
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["*"]
    }
  }
}

```

---

### 📁 目錄結構對照範例

若你有勾選使用 `src/`，專案結構可能如下：

```
demo-next/
├── src/
│   ├── app/
│   ├── components/
│   └── ...
├── tsconfig.json

```

則你就能寫這樣的路徑：

```ts
import { Header } from '@/components/Header'

```

---

## ✅ 總結：建立流程與 alias 設定對照

<table id="bkmrk-%E5%8A%9F%E8%83%BD-%E8%AA%AA%E6%98%8E-%E6%98%AF%E5%90%A6%E8%A8%AD%E5%AE%9A-alias-%28%40%29"><thead><tr><th>功能</th><th>說明</th></tr></thead><tbody><tr><td>是否設定 alias (`@`)</td><td>`create-next-app` 最後一個選項</td></tr><tr><td>若選否，日後可透過 `ts/jsconfig` 設定</td><td>✅ 很簡單，加一段 `"paths"` 即可</td></tr><tr><td>是否需要設定 `next.config.js`</td><td>通常不需要（除非使用 webpack plugin）</td></tr></tbody></table>

---

# 【Next.js】專案結構與檔案說明

以下是根據你提供的 Next.js 專案結構，整理出各資料夾與檔案的用途與說明：

---

### 📁 專案結構與說明表格

<table id="bkmrk-%E8%B7%AF%E5%BE%91-%2F-%E6%AA%94%E6%A1%88-%E9%A1%9E%E5%9E%8B-%E8%AA%AA%E6%98%8E-.next%2F"><thead><tr><th>路徑 / 檔案</th><th>類型</th><th>說明</th></tr></thead><tbody><tr><td>`.next/`</td><td>資料夾</td><td>Next.js **build 輸出資料夾**，會在 `next build` 時產生。內含編譯後的 server 與靜態資源。可部署至伺服器。</td></tr><tr><td>├── `server/`</td><td>資料夾</td><td>Server-side compiled files（如 SSR 頁面、API Routes）。</td></tr><tr><td>├── `static/`</td><td>資料夾</td><td>靜態產出資源（例如圖片、CSS、JS 等）。</td></tr><tr><td>├── `cache/`</td><td>資料夾</td><td>編譯過程中的 cache，提高二次編譯速度。</td></tr><tr><td>├── `build-manifest.json`</td><td>JSON 檔案</td><td>描述每個頁面對應的 JS chunk，供 runtime 載入使用。</td></tr><tr><td>├── `routes-manifest.json`</td><td>JSON 檔案</td><td>儲存頁面與路由資訊的映射表。</td></tr><tr><td>├── `prerender-manifest.json`</td><td>JSON 檔案</td><td>與預先產生的頁面（SSG）有關，例如 `getStaticProps` 結果。</td></tr><tr><td>`node_modules/`</td><td>資料夾</td><td>安裝的 npm 套件。</td></tr><tr><td>`public/`</td><td>資料夾</td><td>放置**靜態資源**（如圖片、SVG），透過 `/` 路徑對外公開，例如：`public/globe.svg` → `/globe.svg`。</td></tr><tr><td>`src/app/`</td><td>資料夾</td><td>使用 **App Router 架構** 的主要入口，代表新架構（取代 `pages/` 目錄）。</td></tr><tr><td>├── `layout.tsx`</td><td>React 元件</td><td>每個 route 的共同 Layout 元件。App Router 中不可或缺。</td></tr><tr><td>├── `page.tsx`</td><td>React 元件</td><td>對應 `/` 路由的頁面內容（Home）。</td></tr><tr><td>├── `globals.css`</td><td>CSS 檔案</td><td>全域 CSS，需在 `layout.tsx` 中引入。</td></tr><tr><td>`favicon.ico`</td><td>圖示檔案</td><td>網站 favicon，會自動用在 `<head>` 中。</td></tr><tr><td>`.gitignore`</td><td>設定檔</td><td>Git 忽略檔案清單。通常會包含 `.next/`、`node_modules/`。</td></tr><tr><td>`next-env.d.ts`</td><td>TS 宣告</td><td>Next.js 產生的 type 設定檔，自動加入必要的 TypeScript 型別。</td></tr><tr><td>`next.config.ts`</td><td>設定檔</td><td>Next.js 專案設定檔，支援自訂建置、路徑、Plugin 等功能。</td></tr><tr><td>`package.json` / `package-lock.json`</td><td>設定檔</td><td>npm 套件定義與鎖定檔。記錄所有依賴與版本。</td></tr><tr><td>`postcss.config.mjs`</td><td>設定檔</td><td>PostCSS 設定，通常與 TailwindCSS 搭配使用。</td></tr><tr><td>`README.md`</td><td>說明檔</td><td>專案說明、安裝方式、開發資訊等。</td></tr><tr><td>`tsconfig.json`</td><td>設定檔</td><td>TypeScript 專案設定，例如路徑 alias、編譯選項等。</td></tr></tbody></table>

---

### 📌 重點說明

<table id="bkmrk-%E5%88%86%E9%A1%9E-%E9%87%8D%E9%BB%9E-%E9%87%8D%E8%A6%81%E7%9B%AE%E9%8C%84-src%2Fapp%2F%EF%BC%88"><thead><tr><th>分類</th><th>重點</th></tr></thead><tbody><tr><td>**重要目錄**</td><td>`src/app/`（使用 App Router 架構）、`public/`（靜態資源）、`.next/`（build 輸出）</td></tr><tr><td>**重要設定檔**</td><td>`next.config.ts`、`tsconfig.json`、`postcss.config.mjs`</td></tr><tr><td>**App Router 必備**</td><td>`layout.tsx` 是必要頁面組件，類似傳統 `_app.tsx + _document.tsx`</td></tr><tr><td>**部署時可忽略**</td><td>`node_modules/`、`.next/cache`、`.gitignore`</td></tr></tbody></table>

---

## 說明layout.tsx 與 page.tsx 關係

`layout.tsx` 與 `page.tsx` 是 Next.js 13+（App Router 架構）中，兩個**核心檔案**，分別扮演不同但互補的角色。

---

## 🧱 簡單說明

<table id="bkmrk-%E6%AA%94%E6%A1%88-%E7%94%A8%E9%80%94-%E8%BC%89%E5%85%A5%E9%A0%BB%E7%8E%87-%E6%AF%94%E5%96%BB-layout"><thead><tr><th>檔案</th><th>用途</th><th>載入頻率</th><th>比喻</th></tr></thead><tbody><tr><td>`layout.tsx`</td><td>定義頁面結構與共用 UI（像是外框）</td><td>只載入一次（不會重複重建）</td><td>「建築物的骨架」</td></tr><tr><td>`page.tsx`</td><td>定義實際頁面內容</td><td>每個頁面都會重新載入</td><td>「房間的內容、家具、住戶」</td></tr></tbody></table>

---

## 🧭 目錄架構範例

```bash
app/
├── layout.tsx         # 所有頁面的共用 Layout
├── page.tsx           # / 路徑的首頁內容
├── about/
│   ├── layout.tsx     # 僅 /about 使用的 layout
│   └── page.tsx       # /about 的內容

```

---

## 🧩 關係與行為

1. **一個 layout 對應多個 page**
    
    
    - 例如 `app/layout.tsx` 包住所有頁面：`/`, `/about`, `/contact`
2. **嵌套 layout**
    
    
    - 子資料夾可有自己的 `layout.tsx`，會**包在上層 layout 裡**
3. **layout 不會因為切換 page 被卸載**
    
    
    - 可以放在 `Header`, `Sidebar`, `Footer` 等共用 UI 元件，效能更佳
4. **page.tsx 是實際的頁面入口**
    
    
    - 每個 URL 都需要一個 `page.tsx` 來當作入口點

---

## 📦 實際範例

### `app/layout.tsx`

```tsx
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <header>共用導覽列</header>
        <main>{children}</main>
        <footer>共用頁尾</footer>
      </body>
    </html>
  )
}

```

### `app/page.tsx`

```tsx
export default function HomePage() {
  return <h1>這是首頁內容</h1>
}

```

👉 此時 `HomePage` 內容會被注入進 `layout.tsx` 的 `{children}` 中。

---

## 🔁 一圖總結

```
layout.tsx
├── Header
├── <main>
│   └── page.tsx 的內容
└── Footer

```

---

## ✅ 補充：layout.tsx 特性

<table id="bkmrk-%E7%89%B9%E6%80%A7-%E6%98%AF%E5%90%A6%E6%94%AF%E6%8F%B4-%E5%82%99%E8%A8%BB-%E6%94%AF%E6%8F%B4-loadin"><thead><tr><th>特性</th><th>是否支援</th><th>備註</th></tr></thead><tbody><tr><td>支援 `loading.tsx`</td><td>✅</td><td>當子頁面在載入時顯示 loading UI</td></tr><tr><td>支援 `error.tsx`</td><td>✅</td><td>包裹此 layout 的錯誤畫面</td></tr><tr><td>支援 `metadata` 設定</td><td>✅</td><td>可以定義 title、description 等</td></tr><tr><td>自動 caching</td><td>✅</td><td>layout 有自動快取，非動態頁效能極佳</td></tr></tbody></table>

---

需要我幫你畫出 layout/page 的巢狀結構流程圖或建立範本專案也可以說一聲。

</body></html>

# 【Next.js】路由機制

Next.js 使用檔案結構（**File System Routing**）搭配 `page.tsx`（或 `page.jsx`）自動產生路由，這是 Next.js 的一大特色，讓你**不需手動寫 route config**，只要建立檔案與資料夾就能對應 URL。

---

## 🗂️ Next.js App Router 的路由機制（以 `app/` 為根）

```bash
app/
├── page.tsx            → /
├── about/
│   └── page.tsx        → /about
├── blog/
│   ├── page.tsx        → /blog
│   └── [slug]/
│       └── page.tsx    → /blog/hello、/blog/123
├── dashboard/
│   ├── layout.tsx      → 包住 dashboard 所有子路由
│   ├── page.tsx        → /dashboard
│   └── settings/
│       └── page.tsx    → /dashboard/settings

```

---

## 🔑 路由對應邏輯

<table id="bkmrk-%E6%AA%94%E6%A1%88%E5%90%8D%E7%A8%B1-%E5%B0%8D%E6%87%89%E8%B7%AF%E5%BE%91-%28url%29-%E8%AA%AA%E6%98%8E-a"><thead><tr><th>檔案名稱</th><th>對應路徑 (URL)</th><th>說明</th></tr></thead><tbody><tr><td>`app/page.tsx`</td><td>`/`</td><td>首頁</td></tr><tr><td>`app/about/page.tsx`</td><td>`/about`</td><td>靜態路由</td></tr><tr><td>`app/blog/[slug]/page.tsx`</td><td>`/blog/:slug`</td><td>動態路由</td></tr><tr><td>`app/dashboard/page.tsx`</td><td>`/dashboard`</td><td>支援巢狀路由</td></tr><tr><td>`app/dashboard/settings/page.tsx`</td><td>`/dashboard/settings`</td><td>巢狀子頁面</td></tr></tbody></table>

---

## 📌 動態路由（Dynamic Routes）

### 範例：

```bash
app/product/[id]/page.tsx

```

訪問 `/product/123`、`/product/abc` 都會進到這個頁面。

### 如何在 component 中取得參數：

```tsx
import { useParams } from 'next/navigation'

export default function ProductPage() {
  const params = useParams()
  return <div>商品 ID：{params.id}</div>
}

```

---

## ⛓️ 巢狀路由與 Layout 配合

```bash
app/
├── layout.tsx             // 全站 layout
├── dashboard/
│   ├── layout.tsx         // dashboard 專屬 layout
│   └── settings/
│       └── page.tsx       // 會套用兩層 layout

```

➡ 這樣 `/dashboard/settings` 的畫面會被兩層 layout 包起來。

---

## 🚫 注意事項

<table id="bkmrk-%E9%99%90%E5%88%B6-%E8%AA%AA%E6%98%8E-page.tsx-%E7%82%BA%E5%BF%85%E8%A6%81%E9%A0%81%E9%9D%A2"><thead><tr><th>限制</th><th>說明</th></tr></thead><tbody><tr><td>`page.tsx` 為必要頁面進入點</td><td>每個路由至少需要一個 `page.tsx`</td></tr><tr><td>不支援動態 catch-all `*`</td><td>請使用 `[...slug]`</td></tr><tr><td>`app/` 下不能使用 `pages/`</td><td>App Router 與 Pages Router 不能共存</td></tr></tbody></table>

---

## ✨ 小技巧

<table id="bkmrk-%E7%9B%AE%E7%9A%84-%E5%AF%A6%E4%BD%9C%E6%96%B9%E5%BC%8F-%E5%8B%95%E6%85%8B%E8%B7%AF%E7%94%B1%EF%BC%88product"><thead><tr><th>目的</th><th>實作方式</th></tr></thead><tbody><tr><td>動態路由（product/:id）</td><td>`app/product/[id]/page.tsx`</td></tr><tr><td>可選參數（可有可無）</td><td>`app/product/[[id]]/page.tsx`</td></tr><tr><td>Catch-all 路由</td><td>`app/docs/[...slug]/page.tsx`</td></tr><tr><td>自定 metadata（如 title）</td><td>使用 `export const metadata`</td></tr></tbody></table>

---

## ✅ 結論

Next.js 的路由系統基於檔案結構：

- **目錄名代表 URL 路徑**
- **`page.tsx` 是每個路由的「入口」**
- **巢狀結構代表巢狀路由**
- **使用 `[]` 定義動態路由**

---

如果你有具體的頁面結構想設計，我可以幫你列出完整檔案路徑與對應 URL 對照表。

# 【Next.js】路由機制(動態)

在 Next.js 中，\*\*Dynamic Routes（動態路由）\*\*允許你根據 URL 中的參數來動態建立頁面，這對於像是部落格文章、使用者頁面、商品詳細頁等非常有用。

---

## 🗂️ 基本概念

在 Next.js 中，**每個檔案都對應一個路由**。當你在 `app/` 或 `pages/` 目錄中使用 **方括號（\[param\]）** 定義檔案名稱時，就代表建立了一個動態路由。

例如：

```
pages/
  └── blog/
        └── [slug].tsx

```

這樣的結構可以對應：

- `/blog/hello-world`
- `/blog/my-second-post`

在 `app/` 目錄中則會是：

```
app/
  └── blog/
        └── [slug]/
              └── page.tsx

```

---

## 💡 Dynamic Routes 實作（`app/`）

### 1️⃣ 建立檔案：`app/post/[id]/page.tsx`

```JavaScript
interface PageProps {
  params: { id: string }
}

export default function Post({ params }: PageProps) {
  return <h1>Post ID: {params.id}</h1>
}

// { params }: PageProps
// { params } => 解構，取出物件的 params 屬性
// :PageProps typescript宣告型別
// 直接簡化成 { params }: { params: { id: string } }
```

---

## 🔁 多層動態路由

支援巢狀結構，例如：

```
app/
  └── user/
        └── [userId]/
              └── post/
                    └── [postId]/
                          └── page.tsx

```

訪問路徑 `/user/1/post/10` 對應的 `params` 為：

```ts
params: {
  userId: '1',
  postId: '10'
}

```

---

## ✅ Catch-all 路由（捕捉所有）

你可以使用 `[...slug]` 來捕捉多層級路徑：

- `app/products/[...slug].tsx` 會匹配 `/products/a`、`/products/a/b/c`

```tsx
interface PageProps {
  params: {
    slug?: string[];
  };
}

export default function ProductPage({ params }: PageProps) {
  return (
    <div>
      <h1>Products Page</h1>
      {JSON.stringify(params.slug)}
    </div>
  );
}
```

如果要可選，可用 `[[...slug]]` 表示非必要。

---

## 📌 小結

<table id="bkmrk-%E9%A1%9E%E5%9E%8B-%E8%AA%AA%E6%98%8E-%E7%AF%84%E4%BE%8B-%5Bparam%5D-%E5%9F%BA%E6%9C%AC%E5%8B%95"><thead><tr><th>類型</th><th>說明</th><th>範例</th></tr></thead><tbody><tr><td>`[param]`</td><td>基本動態路由</td><td>`/post/1` → `[id].tsx`</td></tr><tr><td>`[...param]`</td><td>捕捉多層路徑（Catch-all）</td><td>`/docs/a/b` → `[...slug].tsx`</td></tr><tr><td>`[[...param]]`</td><td>可選 catch-all（Optional）</td><td>`/docs` or `/docs/a`</td></tr></tbody></table>

---

若你想看實際範例或搭配 `getStaticPaths`、`generateStaticParams` 使用，也可以提出，我可以補上。需要嗎？

# 【Next.js】not-found (404)

在 Next.js 中，`not-found` 頁面是用來顯示 **404 頁面**（找不到頁面）用的。從 Next.js 13（App Router）開始，你可以更靈活地**自訂與觸發 not-found**。

---

## 🧭 `not-found` 頁面用途

<table id="bkmrk-%E5%8A%9F%E8%83%BD-%E8%AA%AA%E6%98%8E-%E9%A0%90%E8%A8%AD-404-%E9%A0%81%E9%9D%A2-%E8%A8%AA%E5%95%8F%E4%B8%8D%E5%AD%98"><thead><tr><th>功能</th><th>說明</th></tr></thead><tbody><tr><td>預設 404 頁面</td><td>訪問不存在的路由時自動顯示</td></tr><tr><td>自訂 not-found</td><td>你可以定義自訂的樣式與內容</td></tr><tr><td>手動觸發 404</td><td>在伺服器邏輯中，條件不符時主動觸發 `notFound()`</td></tr></tbody></table>

---

## 📁 App Router (`app/` 目錄) 中的使用方式

### 📌 1. 建立 `not-found.tsx` 頁面

放在你想處理的路由資料夾中，例如：

```
app/
├── blog/
│   ├── [slug]/
│   │   ├── page.tsx
│   │   └── not-found.tsx   👈 對應 `/blog/[slug]` 找不到時

```

### 📦 `app/blog/[slug]/not-found.tsx`

```tsx
export default function NotFound() {
  return (
    <div>
      <h2>這篇文章不存在</h2>
      <a href="/">回首頁</a>
    </div>
  );
}

```

---

### 📌 2. 在 `page.tsx` 中觸發 `notFound()`

```tsx
import { notFound } from 'next/navigation';

export default function BlogPost({ params }: { params: { slug: string } }) {
  const validSlugs = ['a', 'b', 'c'];

  if (!validSlugs.includes(params.slug)) {
    notFound(); // 👈 主動觸發 404 頁面
  }

  return <h1>Blog: {params.slug}</h1>;
}

```

---

## 📁 Global Not Found Page

如果你在 `app/not-found.tsx` 建立，這就是全站共用的 404 頁面。

```
app/
├── not-found.tsx        👈 對應任何未定義頁面

```

---

## 📦 `app/not-found.tsx` 範例

```tsx
export default function GlobalNotFound() {
  return (
    <div style={{ textAlign: 'center', padding: '2rem' }}>
      <h1>404 - 頁面找不到</h1>
      <p>您訪問的頁面可能已被移除或從未存在。</p>
      <a href="/">回首頁</a>
    </div>
  );
}

```

---

## 🧪 測試方式

<table id="bkmrk-%E6%B8%AC%E8%A9%A6%E8%B7%AF%E5%BE%91-%E9%A0%90%E6%9C%9F%E7%B5%90%E6%9E%9C-%2Fblog%2Fxxx-"><thead><tr><th>測試路徑</th><th>預期結果</th></tr></thead><tbody><tr><td>`/blog/xxx`</td><td>若 `xxx` 不存在 → 觸發 `notFound()`</td></tr><tr><td>`/some/random/page`</td><td>找不到任何匹配路由 → global 404</td></tr></tbody></table>

---

## ✅ 小結

<table id="bkmrk-%E5%8A%9F%E8%83%BD-%E7%94%A8%E6%B3%95-%E5%AE%9A%E7%BE%A9-not-found-%E9%A0%81"><thead><tr><th>功能</th><th>用法</th></tr></thead><tbody><tr><td>定義 not-found 頁面</td><td>在 `app/***/not-found.tsx` 中建立</td></tr><tr><td>全域 fallback 頁面</td><td>在 `app/not-found.tsx`</td></tr><tr><td>手動觸發 404</td><td>使用 `notFound()` 函式</td></tr></tbody></table>

# 【Next.js】Private Folders

在 Next.js 中，\*\*Private Folders（私有資料夾）\*\*是指：

> 🔒 **不會被當作路由、自動產生頁面或靜態資源的資料夾**。  
> 這些資料夾的名稱 **開頭是底線 `_` 或雙底線 `__`**，也就是說：

### ✅ 資料夾名稱開頭為 `_` 或 `__` ⇒ Next.js 會自動忽略它

---

## 🔍 什麼是 Private Folders？

這是 Next.js 的一項慣例（convention），用來 **隔離你不想當作頁面處理的輔助程式碼或元件檔案**。

---

## 📁 使用範例

假設你有以下結構：

```
app/
├── page.tsx
├── _utils/              ← ✅ 不會當成路由
│   └── formatter.ts
├── __tests__/           ← ✅ 不會當成路由
│   └── page.test.tsx

```

這些 `_utils/`、`__tests__/` 都是 **Private Folders**：

- **不會產生 `/utils` 路由**
- **不會被包含在 build 出來的頁面中**
- **適合放測試、工具、常數、hooks 等程式碼**

---

## ✅ 實用用途

<table id="bkmrk-%E8%B3%87%E6%96%99%E5%A4%BE%E5%90%8D%E7%A8%B1-%E7%94%A8%E9%80%94%E7%AF%84%E4%BE%8B-_utils%2F-%E6%94%BE"><thead><tr><th>資料夾名稱</th><th>用途範例</th></tr></thead><tbody><tr><td>`_utils/`</td><td>放工具函式（e.g. dateFormat）</td></tr><tr><td>`_hooks/`</td><td>放自訂 React Hook</td></tr><tr><td>`__tests__/`</td><td>放單元測試</td></tr><tr><td>`__mocks__/`</td><td>放 Jest mock 資料</td></tr></tbody></table>

---

## 🧠 注意事項

<table id="bkmrk-%E8%A1%8C%E7%82%BA-%E8%AA%AA%E6%98%8E-%E2%9C%85-%E5%85%81%E8%A8%B1-import-%E4%BD%BF%E7%94%A8"><thead><tr><th>行為</th><th>說明</th></tr></thead><tbody><tr><td>✅ 允許 `import` 使用</td><td>Private folders 裡的檔案仍然可以被引用</td></tr><tr><td>❌ 不會產生 URL</td><td>不會出現像 `/__tests__` 這種頁面</td></tr><tr><td>✅ 適用於 `pages/` 與 `app/` 架構</td><td>兩種路由系統都支援</td></tr><tr><td>⚠️ 無法自訂規則</td><td>Next.js 只會根據 `_` 或 `__` 開頭自動判斷</td></tr></tbody></table>

---

## 📝 範例：使用 `_utils/format.ts`

```ts
// app/_utils/format.ts
export function formatCurrency(amount: number) {
  return `$${amount.toFixed(2)}`
}

```

```tsx
// app/products/page.tsx
import { formatCurrency } from '../_utils/format'

export default function ProductPage() {
  return <p>{formatCurrency(123.456)}</p>
}

```

---

## ✅ 小結

<table id="bkmrk-%E9%A0%85%E7%9B%AE-%E8%AA%AA%E6%98%8E-%E5%91%BD%E5%90%8D%E8%A6%8F%E5%89%87-%E8%B3%87%E6%96%99%E5%A4%BE%E5%90%8D%E7%A8%B1%E9%96%8B%E9%A0%AD%E7%82%BA-"><thead><tr><th>項目</th><th>說明</th></tr></thead><tbody><tr><td>命名規則</td><td>資料夾名稱開頭為 `_` 或 `__`</td></tr><tr><td>功能</td><td>不產生頁面、不加入 build 路由</td></tr><tr><td>適合用途</td><td>工具、hook、測試、mock、常數</td></tr><tr><td>是否能 import</td><td>✅ 可以被引用使用</td></tr></tbody></table>

---

這是一個非常好也非常細緻的問題，牽涉到 JavaScript / TypeScript 社群和工具生態中的 **命名慣例（naming conventions）**，並不是 Next.js 特有的規定，而是社群共識。

---

## 🔍 TL;DR 結論

<table id="bkmrk-%E5%91%BD%E5%90%8D%E5%BD%A2%E5%BC%8F-%E7%94%A8%E9%80%94-%2F-%E6%85%A3%E4%BE%8B-%E4%BE%86%E6%BA%90-_fol"><thead><tr><th>命名形式</th><th>用途 / 慣例</th><th>來源</th></tr></thead><tbody><tr><td>`_folder`</td><td>🔒 私有資料夾，Next.js 慣例（前綴式）</td><td>Next.js、社群通用慣例</td></tr><tr><td>`__tests__`</td><td>✅ 測試資料夾，Jest 標準結構（雙底線前後）</td><td>[Jest 官方](https://jestjs.io/docs/structure)</td></tr><tr><td>`__mocks__`</td><td>✅ Mock 資料夾，Jest 標準</td><td>Jest</td></tr><tr><td>`_filename.ts`</td><td>私有或輔助用檔案（非主模組）</td><td>Node / TypeScript 社群</td></tr></tbody></table>

---

## ✅ 單底線 `_`：**一般表示私有或特殊用途**

- `_utils/`、`_hooks/`、`_internal/`
- 表示：**這不是一個路由，也不是 API 端點，只是輔助邏輯用**

這種用法來自許多框架（Django、Rails、Node.js）與社群習慣。

---

## ✅ 雙底線 `__`：**通常是工具/測試框架的保留命名**

來自 **Jest、React Native、Metro Bundler 等工具**：

- `__tests__` → Jest 會自動找出來執行單元測試
- `__mocks__` → Jest 用來定義自動 mock 資料
- `__snapshots__` → Jest 的 snapshot 測試檔案

👉 **雙底線前後一致**，通常代表「這是特殊目的目錄」，會被工具偵測與處理。

---

## 🧠 命名語意總整理

<table id="bkmrk-%E5%91%BD%E5%90%8D-%E8%AA%9E%E6%84%8F-%E5%B8%B8%E8%A6%8B%E7%94%A8%E9%80%94-_%E5%90%8D%E7%A8%B1-%E7%A7%81%E6%9C%89-%2F-"><thead><tr><th>命名</th><th>語意</th><th>常見用途</th></tr></thead><tbody><tr><td>`_名稱`</td><td>私有 / 忽略 / 特殊用途（Next.js 慣例）</td><td>`_utils`, `_internal`, `_api`</td></tr><tr><td>`__名稱__`</td><td>測試工具專用</td><td>`__tests__`, `__mocks__`</td></tr><tr><td>無底線</td><td>公開模組 / 頁面 / API 等</td><td>`products`, `user`, `api`</td></tr></tbody></table>

---

## ✅ Next.js 怎麼處理這些命名？

<table id="bkmrk-%E8%B3%87%E6%96%99%E5%A4%BE%E5%90%8D%E7%A8%B1-%E6%98%AF%E5%90%A6%E6%9C%83%E8%BD%89%E6%88%90%E8%B7%AF%E7%94%B1%EF%BC%9F-%E7%94%A8%E9%80%94%E5%BB%BA%E8%AD%B0-"><thead><tr><th>資料夾名稱</th><th>是否會轉成路由？</th><th>用途建議</th></tr></thead><tbody><tr><td>`products/`</td><td>✅ 是</td><td>頁面</td></tr><tr><td>`_products/`</td><td>❌ 否</td><td>工具 / 非頁面模組</td></tr><tr><td>`__tests__/`</td><td>❌ 否</td><td>單元測試、快照測試用</td></tr></tbody></table>

---

## 📌 延伸知識：為何不是 `_tests/` 而是 `__tests__`？

這來自 **Unix / Bash 與正規表達式處理的傳統**：

- `__tests__` 這種明確結構更容易在工具中用 glob 匹配，如：
    
    ```bash
    find . -name '__tests__'
    jest "**/__tests__/**/*.test.tsx"
    
    ```

這是 Jest 與 Babel 的官方推薦格式。

---

## ✅ 總結

<table id="bkmrk-%E5%91%BD%E5%90%8D%E9%A1%9E%E5%9E%8B-%E6%84%8F%E5%9C%96-%2F-%E5%B7%A5%E5%85%B7%E6%94%AF%E6%8F%B4-_xxx%2F"><thead><tr><th>命名類型</th><th>意圖 / 工具支援</th></tr></thead><tbody><tr><td>`_xxx/`</td><td>Next.js 私有目錄，僅開頭底線</td></tr><tr><td>`__xxx__/`</td><td>Jest 風格目錄，工具自動識別處理</td></tr><tr><td>`_xxx.ts`</td><td>單一檔案私有語意</td></tr></tbody></table>

---

## 此外通常utils 其他分類資料夾只要不在app 下就不會產生路由，也是一種常用的方法

[![image-1753426662774.png](https://bookstack.treemanou.com/uploads/images/gallery/2025-07/scaled-1680-/WjkEchrWj4prc83H-image-1753426662774.png)](https://bookstack.treemanou.com/uploads/images/gallery/2025-07/WjkEchrWj4prc83H-image-1753426662774.png)

# 【Next.js】Route Groups

在 Next.js 的 **App Router** 架構中，`Route Groups` 是一個非常實用的功能，用來：

> ✅ **分組路由檔案，但不會影響 URL 路徑或頁面路由**。

---

## 🗂️ Route Groups 是什麼？

**語法：用括號 () 包住資料夾名稱**

例如：

```
app/
├── (dashboard)/
│   └── analytics/
│       └── page.tsx    ← URL: /analytics（不會變成 /dashboard/analytics）

```

### ✅ 路徑中的 `(dashboard)` 不會出現在網址上

但它**可以讓你組織頁面、套用 layout**。

---

## 📌 使用場景

<table id="bkmrk-%E4%BD%BF%E7%94%A8%E7%9B%AE%E7%9A%84-%E8%AA%AA%E6%98%8E-%E7%B5%84%E7%B9%94%E9%A0%81%E9%9D%A2%E7%B5%90%E6%A7%8B-%E8%AE%93%E4%BD%A0%E7%9A%84-a"><thead><tr><th>使用目的</th><th>說明</th></tr></thead><tbody><tr><td>**組織頁面結構**</td><td>讓你的 app 資料夾更乾淨、更有結構</td></tr><tr><td>**不同區段共用不同 layout**</td><td>例如 (marketing)、(admin)、(auth) 區塊使用不同 layout</td></tr><tr><td>**不污染 URL**</td><td>URL 不包含 group 名稱，例如 `(admin)/users` → `/users`</td></tr></tbody></table>

---

## ✅ 範例 1：基本 Route Group

```
app/
├── (marketing)/
│   └── home/
│       └── page.tsx

```

**訪問路徑：** `/home` ✅  
**不會是：** `/marketing/home` ❌

---

## ✅ 範例 2：共用 layout 的 Route Group

```
app/
├── (auth)/
│   ├── layout.tsx       ← auth 區塊專用 layout
│   ├── login/
│   │   └── page.tsx     ← `/login`
│   └── register/
│       └── page.tsx     ← `/register`

```

👉 `/login` 和 `/register` 頁面都會套用 `(auth)/layout.tsx`

---

## ✅ 範例 3：巢狀 Route Groups

```
app/
├── (admin)/
│   ├── layout.tsx
│   └── users/
│       └── page.tsx       ← `/users`
├── (marketing)/
│   └── about/
│       └── page.tsx       ← `/about`

```

每個 group 都可以有自己專屬的 `layout.tsx`，但網址仍保持扁平化。

---

## 💡 搭配 `not-found.tsx`、`error.tsx`、`loading.tsx`

你可以在 group 中設：

```
app/
├── (admin)/
│   ├── layout.tsx
│   ├── not-found.tsx
│   ├── error.tsx
│   └── dashboard/
│       └── page.tsx

```

這樣 group 中的所有路由都會共用這些錯誤/狀態處理頁。

---

## 🚫 Route Groups 的注意事項

<table id="bkmrk-%E5%95%8F%E9%A1%8C-%E8%AA%AA%E6%98%8E-group-%E5%90%8D%E7%A8%B1%E4%B8%8D%E8%83%BD%E7%94%A8-%2F-"><thead><tr><th>問題</th><th>說明</th></tr></thead><tbody><tr><td>Group 名稱不能用 `/` 或 `-`</td><td>只能用括號與英文小寫（無實際路徑名稱）</td></tr><tr><td>不能與實體 URL 混用</td><td>`app/(admin)/users` 是 `/users`，**不是** `/admin/users`</td></tr><tr><td>沒有 `generateStaticParams()`</td><td>Route Group 自身不是 route，不能設 `page.tsx`</td></tr></tbody></table>

---

## ✅ 小結

<table id="bkmrk-%E7%89%B9%E6%80%A7-%E8%AA%AA%E6%98%8E-%E8%AA%9E%E6%B3%95-%28group-name"><thead><tr><th>特性</th><th>說明</th></tr></thead><tbody><tr><td>語法</td><td>`(group-name)/`</td></tr><tr><td>是否會影響 URL？</td><td>❌ 不會，純粹是分組與結構管理用途</td></tr><tr><td>支援 layout 嗎？</td><td>✅ 支援，可針對 group 套用專屬 layout</td></tr><tr><td>適合什麼用途？</td><td>管理大型專案，區分功能區域（如 admin/auth/marketing）</td></tr></tbody></table>

---

# 【Next.js】Layout

太好了！以下是為「Next.js 初學者」量身打造的教學文章，主題為：

# 🔍 Next.js App Router 中 `layout.tsx`、`page.tsx`、`children` 的邏輯與實作

---

## 🧠 為什麼要了解這三個東西？

在 Next.js 的 App Router 架構中，整個畫面的內容是由許多層「外殼（layout）」與「內頁（page）」所組合而成。而這個組合的核心，就是這三個角色：

- `layout.tsx`：整體框架的「容器」
- `page.tsx`：實際對應到網址的「頁面內容」
- `children`：layout 內部接收的下一層內容（可能是 page，也可能是下一層 layout）

---

## 🔧 這三者的關係是什麼？

你可以想像畫面組成就像是便當盒：

- `layout.tsx` 就是便當盒（框架）
- `page.tsx` 是便當裡的菜色（內容）
- `children` 是 layout.tsx 中的佔位符，用來「放內容的地方」

👉 換句話說：

> **page.tsx 的畫面內容，會自動傳給上層 layout.tsx 中的 `children` 來顯示**

---

## 🧱 結構範例

```txt
app/
├── layout.tsx          ← 全站框架 Layout
├── page.tsx            ← 首頁 `/`
├── about/
│   └── page.tsx        ← `/about` 頁面

```

---

## 📦 layout.tsx 實際程式碼

```tsx
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <header>這是全站共用的 Header</header>
        <main>{children}</main> {/* 這裡會被 page.tsx 的內容填進來 */}
        <footer>這是共用的 Footer</footer>
      </body>
    </html>
  );
}

```

---

## 📦 page.tsx 實際程式碼

```tsx
export default function HomePage() {
  return <div>這是首頁的內容</div>;
}

```

---

## 💡 瀏覽 `/` 時會發生什麼事？

1. Next.js 根據 URL `/` 找到 `app/page.tsx`
2. 找到對應的 `layout.tsx` 作為頁面外框
3. 把 `page.tsx` 的內容當作 `children` 傳給 `layout.tsx`
4. 畫面最終呈現：

```html
<html lang="en">
  <body>
    <header>這是全站共用的 Header</header>
    <main>
      <div>這是首頁的內容</div>
    </main>
    <footer>這是共用的 Footer</footer>
  </body>
</html>

```

---

## 🔁 多層 layout 怎麼辦？

可以有巢狀 layout 結構，例如：

```txt
app/
├── layout.tsx                  ← 全站 layout
├── (admin)/
│   ├── layout.tsx              ← admin 專區 layout
│   └── dashboard/
│       └── page.tsx            ← `/dashboard` 頁面

```

流程如下：

1. `dashboard/page.tsx` 被載入
2. 它被傳給 `admin/layout.tsx` 的 `children`
3. 然後再傳到最外層 `app/layout.tsx` 的 `children`
4. 最終包成一整頁畫面

---

## ✅ 重點整理

<table id="bkmrk-%E5%85%83%E7%B4%A0-%E8%AA%AA%E6%98%8E-layout.tsx-%E5%AE%9A%E7%BE%A9%E9%A0%81"><thead><tr><th>元素</th><th>說明</th></tr></thead><tbody><tr><td>`layout.tsx`</td><td>定義頁面外框、接收 `children`</td></tr><tr><td>`page.tsx`</td><td>對應 URL 的實際頁面內容</td></tr><tr><td>`children`</td><td>layout.tsx 的佔位符，用來顯示下層內容</td></tr><tr><td>傳遞邏輯順序</td><td>`page.tsx` → 傳給 → `layout.tsx` → 再往上層</td></tr></tbody></table>

---

## 📌 初學者常見疑問

### ❓ Q1: `children` 是誰傳給 layout.tsx 的？

A: 是 Next.js 自動傳進來的，不需要自己傳。

---

### ❓ Q2: 可以有多個 layout.tsx 嗎？

A: 可以。每一層目錄都可以定義自己的 layout.tsx，形成巢狀包裝結構。

---

### ❓ Q3: 如果我不寫 layout.tsx 會怎樣？

A: 畫面會照樣顯示，但你就無法包共用的元件（像是 Header、Footer、Provider）。

---

## ✅ 建議實作順序

1. 建立 `app/layout.tsx`：放 header/footer
2. 建立 `app/page.tsx`：顯示首頁
3. 建立 `app/about/page.tsx`：新增新頁面
4. 觀察 `children` 如何把 page 內容塞進 layout

---

以下是這篇教學的視覺化圖解，讓初學者更清楚了解 `layout.tsx`、`page.tsx`、`children` 在 Next.js App Router 中是如何串接的。

---

## 🖼️ 圖解：`layout.tsx` 如何包住 `page.tsx`

```
          Next.js 組合畫面邏輯流程
┌─────────────────────────────┐
│         app/layout.tsx      │
│ ┌─────────────────────────┐ │
│ │       <html>            │ │
│ │       <body>            │ │
│ │   ┌───────────────┐     │ │
│ │   │  <Header />   │     │ │
│ │   └───────────────┘     │ │
│ │   ┌───────────────┐     │ │
│ │   │  {children}   │◄────┼─┐ ← 自動注入
│ │   └───────────────┘     │ │
│ │   ┌───────────────┐     │ │
│ │   │  <Footer />   │     │ │
│ │   └───────────────┘     │ │
│ │       </body>           │ │
│ │       </html>           │ │
│ └─────────────────────────┘ │
└─────────────────────────────┘
             ▲
             │
      ┌───────────────┐
      │ app/page.tsx  │
      └───────────────┘
      內容例如：
      export default function Page() {
        return <div>Hello</div>;
      }

```

---

## 🧠 圖解說明：

- `layout.tsx` 是整個頁面的外框（包含 `<html>`, `<body>`, Header, Footer）
- `{children}` 是一個「插槽」，Next.js 會自動把 `page.tsx` 的內容放進去
- 你不需要自己寫 props 傳入 `children`，Next.js 幫你處理好
- 如果你有巢狀 layout，每一層都是這樣一層一層包下去

---

### **個頁面不要套用全站的 `layout.tsx`**，該怎麼做？

這是一個非常常見的情境：

> 「在 Next.js App Router 中，**某個頁面不要套用全站的 `layout.tsx`**，該怎麼做？」

---

## 🎯 簡短結論

在 **App Router** 中：

> ❌ **無法直接讓某個頁面「跳過」`app/layout.tsx`**，因為它是**全域的根 layout**，每一頁都會套用。

但有兩個實務解法可以達到 **「看起來沒有 layout」的效果**：

---

## ✅ 解法 1：用 Route Group 分離 layout

[https://bookstack.treemanou.com/books/treemanreact/page/nextjsmultiple-root-layouts](https://bookstack.treemanou.com/books/treemanreact/page/nextjsmultiple-root-layouts)

### 📁 結構：

```
app/
├── layout.tsx                   ← 全站 layout（預設套用）
├── (no-layout)/                 ← 📌 特殊：不繼承全站 layout
│   └── login/
│       ├── layout.tsx           ← 空 layout
│       └── page.tsx             ← `/login`

```

### 📦 `(no-layout)/layout.tsx`

```tsx
export default function NoLayout({ children }: { children: React.ReactNode }) {
  return children; // 👈 直接回傳 children，不包任何框架
}

```

> ✅ 這樣 `/login` 就不會套到 `app/layout.tsx` 了  
> ✅ 它只會用 `(no-layout)/layout.tsx`，你可以視為「不使用 layout」

---

## ✅ 解法 2：在 layout.tsx 裡判斷 route 隱藏部分元件

如果你不想拆資料夾，也可以：

```tsx
'use client'

import { usePathname } from 'next/navigation'
import Header from '@/components/Header'
import Footer from '@/components/Footer'

export default function RootLayout({ children }: { children: React.ReactNode }) {
  const pathname = usePathname()
  const isPlainPage = pathname.startsWith('/login') || pathname.startsWith('/print')

  return (
    <html>
      <body>
        {!isPlainPage && <Header />}
        <main>{children}</main>
        {!isPlainPage && <Footer />}
      </body>
    </html>
  )
}

```

> ✅ `/login` 和 `/print` 等頁面就會不顯示 Header / Footer  
> ❌ 但 layout 本身還是存在，只是條件渲染內容

---

## 📝 比較小結

<table id="bkmrk-%E6%96%B9%E5%BC%8F-%E5%84%AA%E9%BB%9E-%E7%BC%BA%E9%BB%9E-%E2%9C%85-%E7%94%A8-route-g"><thead><tr><th>方式</th><th>優點</th><th>缺點</th></tr></thead><tbody><tr><td>✅ 用 Route Group 拆 layout</td><td>完全不繼承任何 layout</td><td>需要多一層目錄</td></tr><tr><td>✅ layout 判斷條件顯示元件</td><td>不需要改目錄結構</td><td>layout 實際還是存在，略為複雜</td></tr></tbody></table>

---

## 🧠 建議

<table id="bkmrk-%E4%BD%BF%E7%94%A8%E6%83%85%E5%A2%83-%E5%BB%BA%E8%AD%B0%E6%96%B9%E5%BC%8F-%E7%99%BB%E5%85%A5%E9%A0%81%E3%80%81%E5%88%97%E5%8D%B0%E9%A0%81%E7%AD%89%E9%9C%80%E8%A6%81"><thead><tr><th>使用情境</th><th>建議方式</th></tr></thead><tbody><tr><td>登入頁、列印頁等需要極簡畫面</td><td>✅ 用 Route Group 分離 layout</td></tr><tr><td>特定頁只想隱藏 header/footer 等元件</td><td>✅ layout 內用 `usePathname()` 判斷</td></tr></tbody></table>

---

如果你需要範例專案或要把這部分整合進教學文章，我可以幫你補上 Markdown + 圖解，要嗎？

</body></html>

# 【Next.js】Multiple Root Layouts

當你想在 **Next.js App Router** 中讓不同的頁面使用 **不同的根 layout（Multiple Root Layouts）**，你可以透過「**路由分組（Route Groups）**」來達成。

這是一種官方推薦的做法，可以讓你做到：

> ✨ **不同路由區段有不同的外框架（Header、Sidebar、樣式主題...）**，互不影響。

---

## ✅ 為什麼需要 Multiple Root Layouts？

你可能有這些需求：

<table id="bkmrk-%E5%8D%80%E5%A1%8A-%E7%89%B9%E6%80%A7-%2F-%E4%BD%BF%E7%94%A8%E5%85%A8%E7%AB%99%E5%9F%BA%E6%9C%AC-layou"><thead><tr><th>區塊</th><th>特性</th></tr></thead><tbody><tr><td>`/`</td><td>使用全站基本 layout</td></tr><tr><td>`/admin`</td><td>使用管理後台 layout（含側邊欄）</td></tr><tr><td>`/auth`</td><td>不需要 layout，例如登入頁</td></tr></tbody></table>

---

## 🎯 解法：使用 Route Groups 分離 layout

### ✅ 語法關鍵：`(group-name)` ← 這是 **Route Group**

它：

- ✅ 不會出現在 URL 中
- ✅ 可以定義自己的 `layout.tsx`
- ✅ 可以做到多個「Root Layout」共存

---

## 📁 範例結構：三個 Root Layouts

```
app/
├── (site)/
│   ├── layout.tsx         ← 公開區域 layout
│   ├── page.tsx           ← `/`
│   └── about/
│       └── page.tsx       ← `/about`
│
├── (admin)/
│   ├── layout.tsx         ← 管理區 layout（有側邊欄）
│   └── dashboard/
│       └── page.tsx       ← `/dashboard`
│
├── (auth)/
│   ├── layout.tsx         ← 登入註冊 layout（極簡）
│   └── login/
│       └── page.tsx       ← `/login`

```

---

## 🧩 每個 layout.tsx 都是**自己的根 layout**

### `app/(site)/layout.tsx`：

```tsx
export default function SiteLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        <header>網站主選單</header>
        <main>{children}</main>
        <footer>頁尾</footer>
      </body>
    </html>
  );
}

```

---

### `app/(admin)/layout.tsx`：

```tsx
export default function AdminLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        <aside>後台選單</aside>
        <section>{children}</section>
      </body>
    </html>
  );
}

```

---

### `app/(auth)/layout.tsx`：

```tsx
export default function AuthLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        {children} {/* 沒有 header/footer */}
      </body>
    </html>
  );
}

```

---

## 📌 注意事項

<table id="bkmrk-%E5%95%8F%E9%A1%8C-%E8%AA%AA%E6%98%8E-group-%E5%90%8D%E7%A8%B1%E6%9C%83%E5%87%BA%E7%8F%BE%E5%9C%A8%E7%B6%B2%E5%9D%80"><thead><tr><th>問題</th><th>說明</th></tr></thead><tbody><tr><td>Group 名稱會出現在網址嗎？</td><td>❌ 不會出現，只是用來分開 layout 結構</td></tr><tr><td>可以混用 layout 嗎？</td><td>✅ 每個 group 是獨立 root layout</td></tr><tr><td>可以共用 components 嗎？</td><td>✅ 沒問題，只要透過 import 即可</td></tr></tbody></table>

---

## 🧠 小結

<table id="bkmrk-%E6%8A%80%E8%A1%93-%E8%AA%AA%E6%98%8E-route-group-%28x"><thead><tr><th>技術</th><th>說明</th></tr></thead><tbody><tr><td>Route Group `(xxx)`</td><td>幫你在不改網址的前提下拆分 layout 結構</td></tr><tr><td>Multiple Layouts</td><td>實現不同頁面區塊有各自獨立外觀、結構</td></tr><tr><td>頁面掛在哪個 group 就用哪個 layout</td><td>完全分離，互不影響</td></tr></tbody></table>

</body></html>

# 【Next.js】Link 元件

當然可以！

---

# 🔗 Next.js 中的 `<Link>` 元件完整說明（針對 App Router）

---

## 📌 什麼是 `<Link>`？

`<Link>` 是 Next.js 提供的**內部導頁元件**，用來在不同頁面之間切換（而不會重新載入整個網頁）。

它的作用類似於 HTML 的 `<a>` 標籤，但它是：

> ✅ 加上 **預先載入（prefetch）**  
> ✅ 使用 **Client-side navigation（客戶端跳轉）**  
> ✅ 避免整頁重新載入，更快更流暢

---

## 🔧 基本用法

```tsx
import Link from 'next/link'

export default function HomePage() {
  return (
    <div>
      <h1>首頁</h1>
      <Link href="/about">前往 About 頁面</Link>
    </div>
  );
}

```

### ✅ 對應效果：

```html
<a href="/about">前往 About 頁面</a>

```

但點擊時會走 Next.js 的路由系統，不會整頁 reload。

---

## 🧠 為什麼不用 `<a href="/about">`？

因為 `<a>` 是瀏覽器原生跳轉：

- 🔁 整頁重新載入
- ❌ 丟失 React 狀態
- ❌ 破壞 Single Page App 流暢性

而 `<Link>` 則使用 **client-side routing**：

- ⚡ 更快，不會重載頁面
- ✅ 保留 layout 與資料
- ✅ 支援 App Router 的預先載入

---

## 🎯 支援的 props（常見）

<table id="bkmrk-prop-%E8%AA%AA%E6%98%8E-href-%E2%9C%85-%E5%BF%85%E5%A1%AB%EF%BC%8C%E8%B7%B3%E8%BD%89"><thead><tr><th>Prop</th><th>說明</th></tr></thead><tbody><tr><td>`href`</td><td>✅ 必填，跳轉目標</td></tr><tr><td>`prefetch`</td><td>是否預先載入（預設 `true`）</td></tr><tr><td>`replace`</td><td>不加入瀏覽器歷史紀錄（像 `location.replace`）</td></tr><tr><td>`scroll`</td><td>是否自動捲動到頂部（預設 `true`）</td></tr><tr><td>`locale`</td><td>多語系路由時指定語言</td></tr></tbody></table>

---

## ✅ 帶參數用法

```tsx
<Link href="/product/123">商品詳情</Link>

```

對應 `app/product/[id]/page.tsx`

---

## ✅ 包含其他元素（像按鈕）

```tsx
<Link href="/about">
  <button>了解更多</button>
</Link>

```

---

## 🔁 搭配動態路由（含物件參數）

```tsx
<Link href={{ pathname: '/product/[id]', query: { id: '123' } }}>
  查看商品
</Link>

```

---

## 📌 注意事項

<table id="bkmrk-%E8%A6%8F%E5%89%87-%E8%AA%AA%E6%98%8E-%3Clink%3E-%E5%8F%AA%E8%83%BD%E7%94%A8%E6%96%BC%E5%85%A7%E9%83%A8%E8%B7%AF"><thead><tr><th>規則</th><th>說明</th></tr></thead><tbody><tr><td>`<Link>` 只能用於內部路由</td><td>外部網站請用 `<a href="https://...">`</td></tr><tr><td>不需要手動加 `as`</td><td>App Router 不需要像舊版 Pages Router 那樣加 `as`</td></tr><tr><td>可以搭配 `usePathname()`</td><td>用來比對當前路徑，製作「選單高亮」</td></tr></tbody></table>

---

## 🧠 實務建議

<table id="bkmrk-%E4%BD%BF%E7%94%A8%E5%A0%B4%E6%99%AF-%E5%BB%BA%E8%AD%B0%E5%AF%AB%E6%B3%95-%E5%88%87%E6%8F%9B%E5%85%A7%E9%83%A8%E9%A0%81%E9%9D%A2-%E7%94%A8-%3C"><thead><tr><th>使用場景</th><th>建議寫法</th></tr></thead><tbody><tr><td>切換內部頁面</td><td>用 `<Link href="/about">`</td></tr><tr><td>導向外部網站</td><td>用 `<a href="https://xx">`</td></tr><tr><td>高亮當前路徑</td><td>搭配 `usePathname()`</td></tr></tbody></table>

---

## ✅ 小結

<table id="bkmrk-%E5%8A%9F%E8%83%BD-%3Clink%3E-%E8%B7%AF%E7%94%B1%E5%88%87%E6%8F%9B-%E2%9C%85-cli"><thead><tr><th>功能</th><th>`<Link>`</th></tr></thead><tbody><tr><td>路由切換</td><td>✅ Client-side navigation</td></tr><tr><td>預先載入</td><td>✅ 支援 prefetch</td></tr><tr><td>整頁 reload</td><td>❌ 不會 reload，保持 SPA 流暢性</td></tr><tr><td>搭配 layout</td><td>✅ Layout 不會重新渲染</td></tr></tbody></table>

---

在 Next.js 中，**Active Link（目前頁面導覽高亮）** 是一個常見的 UI 需求。當使用者正在某個頁面時，對應的導覽列連結（nav bar）應該加上「目前頁面樣式」來提示使用者。

---

# 🔍 Active Link 是什麼？

就是讓當前頁面的導覽項目加上特殊樣式，例如：

```html
<a href="/about" class="active">About</a>

```

讓使用者知道「我現在在這個頁面」。

---

## ✅ Next.js 怎麼做 Active Link？

Next.js 並**不自帶** active class 功能，需要我們手動處理。但 App Router 搭配 `usePathname()` 非常方便。

---

## 📦 使用 `usePathname()` 判斷當前路徑

### 🔧 範例：`ActiveLink.tsx` 元件

```JavaScript
// components/ActiveLink.tsx
"use client";

import Link from "next/link";
import { usePathname } from "next/navigation";
import clsx from "clsx"; // optional, 幫助管理 class

type Props = {
  href: string;
  children: React.ReactNode;
};

export default function ActiveLink({ href, children }: Props) {
  const pathname = usePathname();
  const isActive = pathname === href;

  return (
    <Link
      href={href}
      className={clsx("px-4 py-2", {
        "text-blue-600 font-bold underline": isActive,
        "text-gray-600": !isActive,
      })}
    >
      {children}
    </Link>
  );
}

```

---

## 📄 使用方式

```JavaScript
// app/layout.tsx 或其他 Nav 元件中
import ActiveLink from "@/components/ActiveLink";

export default function NavBar() {
  return (
    <nav className="flex gap-4">
      <ActiveLink href="/">Home</ActiveLink>
      <ActiveLink href="/about">About</ActiveLink>
      <ActiveLink href="/products">Products</ActiveLink>
    </nav>
  );
}

```

---

## 🧠 小技巧

- 路徑可以使用 `startsWith()` 判斷「巢狀頁面」也高亮
    
    ```tsx
    const isActive = pathname.startsWith(href)
    
    ```
- 可以抽出 `activeClassName` 與 `className` 讓元件更彈性

---

## ✅ 顯示效果

當你瀏覽 `/products` 頁面時：

```html
<a href="/products" class="text-blue-600 font-bold underline">

```

---

## 📦 若你使用 Tailwind，可用 `clsx` 管理 class：

```bash
pnpm add clsx
# or
npm install clsx

```

---

## 📝 小結

<table id="bkmrk-%E5%B7%A5%E5%85%B7-%E7%94%A8%E9%80%94-usepathname%28%29-"><thead><tr><th>工具</th><th>用途</th></tr></thead><tbody><tr><td>`usePathname()`</td><td>取得目前 URL 路徑</td></tr><tr><td>`clsx`</td><td>根據條件切換 class</td></tr><tr><td>自訂 `ActiveLink`</td><td>建立可重用的 active 判斷元件</td></tr></tbody></table>

---

# 【Next.js】metadata 設定總覽

以下是 Next.js App Router 中 `metadata` 常用設定的整理表，適合初學者參考與實作時使用。

---

# 📘 Next.js App Router `metadata` 設定總覽

> 用於自動產出 SEO-friendly 的 `<title>`, `<meta>`, `<link>` 等標籤

---

## 🧱 基本結構

```ts
export const metadata: Metadata = {
  title: "單一頁面標題",
  description: "這是這一頁的描述",
};

```

會輸出：

```html
<title>單一頁面標題</title>
<meta name="description" content="這是這一頁的描述">

```

---

## 🔁 多層 Layout 組合使用：template + default

```ts
// app/layout.tsx
export const metadata = {
  title: {
    default: "My Site",
    template: "%s | My Site",
  },
};

```

```ts
// app/about/page.tsx
export const metadata = {
  title: "About",
};

```

💡 實際輸出：

```html
<title>About | My Site</title>

```

---

## 🚫 絕對標題覆蓋 template

```ts
export const metadata = {
  title: {
    absolute: "登入中，請稍候...",
  },
};

```

💡 實際輸出：

```html
<title>登入中，請稍候...</title>

```

---

## 🌐 設定 Open Graph、Twitter Card

```ts
export const metadata = {
  title: "About",
  description: "關於我們的介紹",
  openGraph: {
    title: "About Page",
    description: "OpenGraph 專用描述",
    url: "https://example.com/about",
    siteName: "My Site",
    images: [
      {
        url: "https://example.com/og-image.jpg",
        width: 1200,
        height: 630,
      },
    ],
  },
  twitter: {
    card: "summary_large_image",
    title: "About Page",
    description: "Twitter 用的描述",
    images: ["https://example.com/twitter-image.jpg"],
  },
};

```

---

## 🔒 禁止搜尋引擎索引（noindex）

```ts
export const metadata = {
  robots: {
    index: false,
    follow: false,
  },
};

```

💡 實際輸出：

```html
<meta name="robots" content="noindex, nofollow">

```

---

## 🧭 設定 favicon 與 manifest

```ts
export const metadata = {
  icons: {
    icon: "/favicon.ico",
    shortcut: "/shortcut-icon.png",
    apple: "/apple-touch-icon.png",
  },
  manifest: "/site.webmanifest",
};

```

---

## ✅ 常見欄位對照表

<table id="bkmrk-%E6%AC%84%E4%BD%8D%E5%90%8D%E7%A8%B1-%E8%AA%AA%E6%98%8E-title-%E9%A0%81%E9%9D%A2%E6%A8%99%E9%A1%8C%EF%BC%8C%E5%8F%AF"><thead><tr><th>欄位名稱</th><th>說明</th></tr></thead><tbody><tr><td>`title`</td><td>頁面標題，可以是 string 或物件</td></tr><tr><td>`description`</td><td>meta 描述，用於搜尋結果</td></tr><tr><td>`openGraph`</td><td>Facebook/LinkedIn 等分享資訊</td></tr><tr><td>`twitter`</td><td>Twitter 分享資訊</td></tr><tr><td>`robots`</td><td>是否讓搜尋引擎索引這頁</td></tr><tr><td>`icons`</td><td>頁籤圖示、apple icon 等</td></tr><tr><td>`manifest`</td><td>PWA 設定檔</td></tr></tbody></table>

---

## 📦 使用建議

<table id="bkmrk-%E4%BD%BF%E7%94%A8%E6%83%85%E5%A2%83-%E5%BB%BA%E8%AD%B0%E8%A8%AD%E5%AE%9A-%E4%B8%80%E8%88%AC%E9%A0%81%E9%9D%A2-title"><thead><tr><th>使用情境</th><th>建議設定</th></tr></thead><tbody><tr><td>一般頁面</td><td>`title` + `description` 即可</td></tr><tr><td>分享卡片 / 社群預覽</td><td>加上 `openGraph` 與 `twitter`</td></tr><tr><td>後台或登入頁</td><td>可用 `title.absolute` + `noindex`</td></tr></tbody></table>

---

當然可以！下面我將清楚說明 Next.js `metadata` 中的 `openGraph` 與 `twitter` 欄位的用途、格式與實戰例子，幫助你製作具有社群分享預覽功能的頁面。

---

# 🌐 `openGraph` 與 `twitter` 是什麼？

當你把一個網頁連結貼到 **Facebook、Line、Slack、Twitter 等社群平台**，它會自動產生「預覽卡片」：

> ✅ 圖片  
> ✅ 標題  
> ✅ 描述  
> ✅ 網站名稱

這些內容的來源就是：

- Facebook、Line：讀取 **Open Graph meta tags**
- Twitter：讀取 **Twitter Card meta tags**

Next.js 的 `metadata.openGraph` 與 `metadata.twitter` 就是自動產生這些 tags 的設定方法。

---

## 🧱 `openGraph` 結構（用於 Facebook / Line / LinkedIn）

```ts
export const metadata = {
  openGraph: {
    title: "產品名稱 - 超好用的工具",
    description: "這是一款改變你人生的產品。",
    url: "https://yourdomain.com/product/123",
    siteName: "My Cool App",
    type: "website", // 常見還有: 'article', 'product', 'profile'
    locale: "zh_TW",
    images: [
      {
        url: "https://yourdomain.com/images/product-123.jpg",
        width: 1200,
        height: 630,
        alt: "產品圖說",
      },
    ],
  },
};

```

🔽 對應輸出的 HTML meta：

```html
<meta property="og:title" content="產品名稱 - 超好用的工具" />
<meta property="og:description" content="這是一款改變你人生的產品。" />
<meta property="og:url" content="https://yourdomain.com/product/123" />
<meta property="og:site_name" content="My Cool App" />
<meta property="og:type" content="website" />
<meta property="og:locale" content="zh_TW" />
<meta property="og:image" content="https://yourdomain.com/images/product-123.jpg" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content="產品圖說" />

```

---

## 🐦 `twitter` 結構（用於 Twitter）

```ts
export const metadata = {
  twitter: {
    card: "summary_large_image", // summary / summary_large_image / app / player
    title: "這是商品標題",
    description: "這是商品描述",
    images: ["https://yourdomain.com/images/product-123.jpg"],
    creator: "@your_twitter_id", // 可選
  },
};

```

🔽 對應輸出的 HTML meta：

```html
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="這是商品標題" />
<meta name="twitter:description" content="這是商品描述" />
<meta name="twitter:image" content="https://yourdomain.com/images/product-123.jpg" />
<meta name="twitter:creator" content="@your_twitter_id" />

```

---

## ✅ 圖片尺寸建議

<table id="bkmrk-%E5%B9%B3%E5%8F%B0-%E5%B0%BA%E5%AF%B8%E5%BB%BA%E8%AD%B0-facebook-120"><thead><tr><th>平台</th><th>尺寸建議</th></tr></thead><tbody><tr><td>Facebook</td><td>1200×630 px（1.91:1 長方形）</td></tr><tr><td>Twitter</td><td>1200×628 px（summary\_large\_image）</td></tr><tr><td>LINE</td><td>同 Facebook（讀 og:image）</td></tr></tbody></table>

---

## 🧪 實戰檢查工具

<table id="bkmrk-%E5%B7%A5%E5%85%B7%E5%90%8D%E7%A8%B1-%E9%80%A3%E7%B5%90-facebook-deb"><thead><tr><th>工具名稱</th><th>連結</th></tr></thead><tbody><tr><td>Facebook Debugger</td><td>[https://developers.facebook.com/tools/debug/](https://developers.facebook.com/tools/debug/)</td></tr><tr><td>Twitter Card Validator</td><td>[https://cards-dev.twitter.com/validator](https://cards-dev.twitter.com/validator)</td></tr><tr><td>OpenGraph Preview</td><td>[https://www.opengraph.xyz](https://www.opengraph.xyz/)</td></tr></tbody></table>

---

## 🧠 小結

<table id="bkmrk-%E9%A0%85%E7%9B%AE-opengraph-twitter"><thead><tr><th>項目</th><th>openGraph</th><th>twitter</th></tr></thead><tbody><tr><td>使用平台</td><td>Facebook, Line, LinkedIn, Slack 等</td><td>Twitter 專用</td></tr><tr><td>支援圖片</td><td>✅ 多張圖片</td><td>✅ 單張或多張</td></tr><tr><td>文字自訂</td><td>✅ title, description, siteName 等</td><td>✅ title, description, creator 等</td></tr><tr><td>SEO 關聯</td><td>✅ 有助於點擊率與品牌識別</td><td>✅ Twitter 分享時更具吸引力</td></tr></tbody></table>

---

如果你需要我產出一個 Next.js 頁面範例，實作一頁完整的 SEO + 分享卡片（含圖片），我可以幫你快速產一份喔！要嗎？

# 【Next.js】常用 scripts  說明

這段 `"scripts"` 通常定義在你的 `package.json` 中，是 **Next.js** 專案開發流程的基本指令集。下面我會逐一說明每個指令的用途、情境，以及何時使用：

---

## 📜 `"scripts"` 說明

```json
"scripts": {
  "dev": "next dev",
  "build": "next build",
  "start": "next start",
  "lint": "next lint"
}

```

---

### 1. `"dev": "next dev"`

✅ **本地開發環境**使用，啟動熱重載伺服器。

- 🔁 支援 hot reload（你改檔案會自動重新載入）
- 🌐 開啟 [http://localhost:3000](http://localhost:3000/)
- ✅ 用於：開發、除錯、畫面確認、檢查 API

🧪 **情境：**

```bash
npm run dev
# 或
pnpm dev

```

---

### 2. `"build": "next build"`

🔨 **打包應用程式**，產生 `.next/` 目錄，準備部署。

- 會做：
    
    
    - 頁面預先產生（SSG）
    - Server-side 轉譯編譯（SSR）
    - Minify, tree shaking, code split...
- ❌ 不會啟動伺服器

🧪 **情境：**

```bash
npm run build
# -> 打包專案供 production 用

```

---

### 3. `"start": "next start"`

🚀 **啟動 Production 模式伺服器**

- 需要先執行過 `next build`
- 適合用於正式環境或模擬正式環境（staging）
- 沒有 hot reload（因為是 production）

🧪 **情境：**

```bash
npm run build
npm start
# -> 用於正式上線或部署伺服器中

```

---

### 4. `"lint": "next lint"`

🔍 使用 Next.js 的內建 ESLint 設定檢查程式碼品質。

- 檢查：變數命名、語法錯誤、不一致縮排
- 如果你有設定 ESLint 設定檔（如 `.eslintrc.js`）會套用規則

🧪 **情境：**

```bash
npm run lint
# -> 在部署前自動檢查程式碼品質

```

---

## 🧠 小結（對照表）

<table id="bkmrk-%E6%8C%87%E4%BB%A4-%E7%94%A8%E9%80%94-%E5%9F%B7%E8%A1%8C%E6%99%82%E6%A9%9F-%E6%98%AF%E5%90%A6%E6%AD%A3%E5%BC%8F%E7%94%A8%E9%80%94-de"><thead><tr><th>指令</th><th>用途</th><th>執行時機</th><th>是否正式用途</th></tr></thead><tbody><tr><td>`dev`</td><td>啟動開發伺服器</td><td>開發階段</td><td>❌</td></tr><tr><td>`build`</td><td>建立正式環境可執行檔</td><td>上線前打包</td><td>✅</td></tr><tr><td>`start`</td><td>啟動正式伺服器</td><td>上線後／模擬上線</td><td>✅</td></tr><tr><td>`lint`</td><td>檢查語法與格式</td><td>開發中、CI 中自動化流程檢查</td><td>✅</td></tr></tbody></table>

---

如果你想再加上測試、自動部署或 CI/CD 的 script，我也可以幫你補上建議範本。需要嗎？

# 【Next.js】 部署模式總覽

下面整理 **Next.js 的主要部署類型**、每種模式的特性與常見應用場景，並特別解釋 **standalone** 是什麼以及它適合的情境。

---

## 1️⃣ Next.js 部署模式總覽

<table id="bkmrk-%E9%83%A8%E7%BD%B2%E9%A1%9E%E5%9E%8B-next.config.js-" style="width: 809px;"><thead><tr><th style="width: 106px;">部署類型</th><th style="width: 141px;">`next.config.js` / 指令</th><th style="width: 167px;">產物</th><th style="width: 395px;">特性與適用場景</th></tr></thead><tbody><tr><td style="width: 106px;">**1. Node Server (傳統模式)**</td><td style="width: 141px;">`next build && next start`</td><td style="width: 167px;">`.next/`</td><td style="width: 395px;">**預設**，完整支援 SSR、ISR、API Routes。部署到任何 Node 環境（PM2、Docker、K8s、AWS EC2、GCP、Heroku…）。</td></tr><tr><td style="width: 106px;">**2. Standalone**</td><td style="width: 141px;">`output: 'standalone'` + `next build`</td><td style="width: 167px;">`.next/standalone` + `.next/static`</td><td style="width: 395px;">只輸出**最小可執行檔**（server.js + 必要檔案），方便 Docker/容器化。功能與 Node Server 相同（SSR/ISR/API Routes 皆可）。</td></tr><tr><td style="width: 106px;">**3. Static Export**</td><td style="width: 141px;">`next export` 或 `output: 'export'`</td><td style="width: 167px;">`out/`</td><td style="width: 395px;">完全靜態 HTML/CSS/JS。無 SSR/ISR/API Routes。適合純內容網站、部落格、文件站。</td></tr><tr><td style="width: 106px;">**4. Edge Runtime / Vercel**</td><td style="width: 141px;">`runtime: 'edge'` 或自動判斷</td><td style="width: 167px;">Vercel Edge Functions (或 Cloudflare Workers)</td><td style="width: 395px;">以 Web 標準 API 為基礎，無 Node built-ins。啟動極快、全球節點分布，適合 Middleware、即時驗證、A/B 測試。</td></tr><tr><td style="width: 106px;">**5. Serverless Functions**</td><td style="width: 141px;">Vercel / AWS Lambda</td><td style="width: 167px;">Lambda zip</td><td style="width: 395px;">每個頁面/Route 轉成獨立 Serverless Function。可自動擴展，適合不維護長駐伺服器的架構。</td></tr><tr><td style="width: 106px;">**6. Custom Server**</td><td style="width: 141px;">自行建立 `server.js` 並 `next()`</td><td style="width: 167px;">`.next/`</td><td style="width: 395px;">自定義 Node 伺服器，整合 Koa/Express/Fastify，做複雜路由或中介層。</td></tr></tbody></table>

> <span style="background-color: #ffffff;">註：模式之間可以組合，例如 </span>**Node Server + Standalone**<span style="background-color: #ffffff;">、</span>**Vercel Serverless + Edge Middleware**<span style="background-color: #ffffff;">。</span>

---

<table id="bkmrk-%E6%A8%A1%E5%BC%8F-%E9%9C%80-node.js-runtime"><thead><tr><th>模式</th><th>需 Node.js Runtime</th><th>常見部署基底</th><th>代表場景</th></tr></thead><tbody><tr><td>Node Server</td><td>✅</td><td>Node image / VM</td><td>全功能 SSR/ISR + API</td></tr><tr><td>Standalone</td><td>✅ (但包體精簡)</td><td>Node image / VM</td><td>Docker/K8s 輕量部署</td></tr><tr><td>Static Export</td><td>❌</td><td>Nginx、GitHub Pages、S3+CDN</td><td>純靜態內容</td></tr><tr><td>Edge Runtime</td><td>❌ (Web Worker API)</td><td>Vercel Edge / Cloudflare Workers</td><td>全球低延遲運算、Middleware</td></tr><tr><td>Serverless Functions</td><td>✅ (平台提供)</td><td>AWS Lambda / Vercel Functions</td><td>無伺服器、自動擴展</td></tr><tr><td>Custom Server</td><td>✅</td><td>Node image / VM</td><td>自訂路由、混合架構</td></tr></tbody></table>

## 2️⃣ 各模式詳解

### ① **Node Server (預設)**

- **啟動**：`next build && next start`
- **產物**：`.next/` 目錄
- **特性**：完整支援 SSR、ISR、API Routes。
- **適用**：自行管理伺服器（EC2、Kubernetes、Docker Swarm、Heroku…）。
- **優缺**：最彈性，但部署包體積較大，需要完整 node\_modules。

---

### ② **Standalone** ⭐

- **設定**：
    
    ```js
    // next.config.js
    module.exports = {
      output: 'standalone',
    }
    
    ```
- **產物**：
    
    ```
    .next/standalone      # 精簡 Node 專案，可直接 node server.js
    .next/static          # 靜態資源
    
    ```
- **原理**：
    
    
    - `next build` 會分析依賴，只把執行需要的檔案、node\_modules 打包進 `standalone`。
    - 不再需要完整專案原始碼，也不必把整個 node\_modules 打進 Docker。
- **適用場景**：
    
    
    - Docker / 容器化：  
        只複製 `.next/standalone` + `.next/static`，image 更小、build 更快。
    - Kubernetes / ECS：  
        配合多階段 Docker build，把 `RUN npm ci` 階段與最終執行環境分離。
- **功能**：與 Node Server 相同（SSR / ISR / API Routes 全支援）。

---

### ③ **Static Export**

- **指令**：`next build && next export`
- **產物**：`out/` 完全靜態
- **限制**：無 SSR、無 API Routes、無 ISR。
- **適用**：
    
    
    - 部落格、說明文件、Landing Page
    - 部署到任何靜態空間（S3+CloudFront、GitHub Pages、Netlify）。

---

### ④ **Edge Runtime / Middleware**

- **用法**：在頁面或 route handler 中 `export const runtime = 'edge'`
- **部署**：Vercel Edge Functions、Cloudflare Workers、Deno Deploy。
- **特性**：全球節點極低延遲；不能用 Node built-ins。
- **適用**：
    
    
    - 驗證/權限檢查
    - A/B 測試、URL 轉向
    - 即時 Header/Cookie 操作

---

### ⑤ **Serverless Functions (Vercel / AWS Lambda)**

- **部署**：Vercel 自動化，或 `serverless-http` 將 Next 整包轉為 Lambda。
- **特性**：
    
    
    - 每個頁面/路由轉成獨立 Lambda。
    - 自動擴展、免維運。
- **適用**：
    
    
    - 高併發但流量不穩定。
    - 只需 Node 短期計算、不需長連線。

---

### ⑥ **Custom Server**

- **方式**：建立 `server.js`
    
    ```js
    const express = require('express')
    const next = require('next')
    const app = next({ dev: false })
    const handle = app.getRequestHandler()
    app.prepare().then(() => {
      const server = express()
      server.get('/health', (req, res) => res.send('ok'))
      server.all('*', (req, res) => handle(req, res))
      server.listen(3000)
    })
    
    ```
- **適用**：
    
    
    - 需要與既有 Node 架構整合。
    - 自訂路由、中介層或 WebSocket。

---

## 3️⃣ 如何選擇

<table id="bkmrk-%E9%9C%80%E6%B1%82-%E5%BB%BA%E8%AD%B0%E6%A8%A1%E5%BC%8F-%E4%BC%81%E6%A5%AD%E5%85%A7%E9%83%A8%E3%80%81%E8%87%AA%E7%AE%A1-dock"><thead><tr><th>需求</th><th>建議模式</th></tr></thead><tbody><tr><td>**企業內部、自管 Docker/K8s**</td><td>**Standalone (Node Server)**：產物小、部署快</td></tr><tr><td>**純靜態內容、無後端邏輯**</td><td>**Static Export**</td></tr><tr><td>**全球極低延遲、近使用者運算**</td><td>**Edge Runtime / Middleware**</td></tr><tr><td>**彈性擴展、免維運**</td><td>**Serverless Functions (Vercel / AWS Lambda)**</td></tr><tr><td>**需與自訂 Node 架構整合**</td><td>**Custom Server**</td></tr></tbody></table>

---

### ✅ 總結

- **Standalone** 是 **Node Server 模式的輕量化打包**，非常適合容器化部署。
- 如果要保留完整 SSR、ISR、API Routes，又希望 **映像檔小、啟動快**，選 **Standalone** 幾乎是最佳解。
- 其他模式（Static Export、Edge、Serverless）則依你的應用是否需要即時運算、全球分佈或純靜態內容來決定。

---

## 📊 Next.js 部署模式功能支援度

<table id="bkmrk-%E9%83%A8%E7%BD%B2%E6%A8%A1%E5%BC%8F-%E9%9C%80%E8%A6%81-node-runtime"><thead><tr><th>部署模式</th><th>需要 Node Runtime</th><th>**SSR**</th><th>**ISR**</th><th>**API Routes**</th><th>適用場景</th></tr></thead><tbody><tr><td>**Node Server** (`next build && next start`)</td><td>✅</td><td>✅</td><td>✅</td><td>✅</td><td>傳統部署，功能完整。適合自管伺服器、Docker、K8s。</td></tr><tr><td>**Standalone** (`output: 'standalone'`)</td><td>✅</td><td>✅</td><td>✅</td><td>✅</td><td>與 Node Server 相同，但打包精簡，適合容器化。</td></tr><tr><td>**Static Export** (`next export`)</td><td>❌</td><td>❌</td><td>❌</td><td>❌</td><td>只能輸出純 HTML/JS/CSS。適合靜態網站（部落格、Landing Page）。</td></tr><tr><td>**Serverless Functions** (Vercel / AWS Lambda)</td><td>✅（平台提供）</td><td>✅</td><td>✅</td><td>✅</td><td>拆分成獨立 Functions，自動擴展。適合流量不穩的專案。</td></tr><tr><td>**Edge Runtime** (`runtime: 'edge'`)</td><td>❌（Web API 而非 Node）</td><td>⚠️ 部分支援（僅 Server Components，可撈 API）</td><td>❌</td><td>⚠️ 部分支援（需 Web API，不支援 Node drivers）</td><td>全球低延遲運算、Middleware、A/B 測試。</td></tr><tr><td>**Custom Server** (Express/Koa/Fastify + Next)</td><td>✅</td><td>✅</td><td>✅</td><td>✅</td><td>自訂路由/中介層，整合既有 Node 架構。</td></tr></tbody></table>

---

## 📌 補充說明

- **Static Export**：因為沒有 Node 伺服器 → **無法即時渲染**（SSR）、**無法再生**（ISR）、**無 API Routes**。
- **Edge Runtime**：
    
    
    - 可以做 SSR（例如 Server Components 即時抓資料），但**無 Node API**（不能用 `fs`、原生 DB drivers）。
    - 適合處理 Header、Cookie、權限驗證、低延遲資料讀取。
- **Serverless Functions**：Vercel/AWS 會把 SSR/ISR/API Route 拆成獨立 Function → 自動擴展，但要注意冷啟動與 DB 連線池。

---

## 🎯 總結

- **要完整功能（SSR/ISR/API Routes）** → 用 **Node Server** 或 **Standalone**。
- **要純靜態內容** → 用 **Static Export**。
- **要全球低延遲** → 用 **Edge Runtime**（但功能有限制）。
- **要免維運，自動擴展** → 用 **Serverless Functions**（Vercel、AWS Lambda）。

---

# 【名詞解釋】

# 【名詞解釋】SSR、ISR、API Routes

這三個名詞（**SSR、ISR、API Routes**）是 Next.js 的核心特性，常常讓人混淆。下面逐一拆解，說明它們的概念、運作方式、優缺點與應用場景。

---

## 1️⃣ SSR（Server-Side Rendering）

**Server-Side Rendering = 伺服端渲染**

- **運作方式**  
    每次使用者請求頁面時，Next.js 在 **伺服器端** 執行 React 元件，生成完整 HTML，再回傳給瀏覽器。
- **關鍵點**
    
    
    - 每次請求都會「即時」生成頁面內容。
    - 適合需要最新數據的頁面（例如：即時報價、使用者 Dashboard）。

**程式碼範例**：

```ts
// pages/index.tsx
export async function getServerSideProps() {
  const data = await fetch('https://api.example.com/data').then(res => res.json())
  return { props: { data } }
}

export default function Home({ data }) {
  return <div>最新數據：{data.value}</div>
}

```

**優點**

- SEO 友好（HTML 完整輸出）。
- 確保每次都是最新資料。

**缺點**

- 每次請求都要跑後端 → 伺服器壓力大。
- 延遲較高，不適合高流量純靜態內容。

**適用場景**

- 會員頁面、個人化內容、後台管理介面。

---

## 2️⃣ ISR（Incremental Static Regeneration）

**ISR = 增量式靜態再生**

- **運作方式**
    
    
    - 頁面第一次請求時，Next.js 會生成靜態 HTML 並快取。
    - 之後相同頁面會直接回傳快取內容（像 SSG 一樣快）。
    - 在設定的 `revalidate` 時間後，Next.js 會在背景「重新生成」頁面，並更新快取。

**程式碼範例**：

```ts
// pages/blog/[id].tsx
export async function getStaticProps() {
  const post = await fetch('https://api.example.com/post/1').then(res => res.json())
  return {
    props: { post },
    revalidate: 60, // 每 60 秒重新生成一次
  }
}

```

**優點**

- 讀取速度快（靜態 HTML）。
- 自動更新內容，避免手動重建整個網站。
- 減輕伺服器壓力。

**缺點**

- 更新不是即時的，而是取決於 `revalidate` 時間。
- 若內容更新頻繁，可能仍需要 SSR。

**適用場景**

- 部落格文章、商品列表、新聞頁面。

---

## 3️⃣ API Routes

**API Routes = 內建後端 API**

- **運作方式**  
    在 Next.js 專案內建立 `pages/api/*` 或 `app/api/*/route.ts`，即可定義一個 API endpoint。
    
    
    - 這些 Route 會在 **伺服器端執行**，輸出 JSON 或其他格式。
    - 不需要額外架設 Express/Koa，直接用 Next.js 提供的 Node Runtime。

**程式碼範例**：

```ts
// pages/api/hello.ts
export default function handler(req, res) {
  res.status(200).json({ message: 'Hello API!' })
}

```

或（App Router 格式）：

```ts
// app/api/hello/route.ts
export async function GET() {
  return Response.json({ message: 'Hello API!' })
}

```

**優點**

- 與前端同專案，部署簡單。
- 適合輕量後端邏輯（驗證、整合第三方 API、Form 提交）。

**缺點**

- 不適合複雜業務邏輯（相比 Spring Boot / NestJS）。
- 在 Serverless 環境（Vercel、Lambda）會有冷啟動問題。

**適用場景**

- Contact Form API、登入驗證、前端代理 API。

---

## 4️⃣ 對比表

<table id="bkmrk-%E7%89%B9%E6%80%A7-ssr-isr-api-route"><thead><tr><th>特性</th><th>**SSR**</th><th>**ISR**</th><th>**API Routes**</th></tr></thead><tbody><tr><td>用途</td><td>動態頁面渲染</td><td>靜態頁面快取 + 定時更新</td><td>提供後端 API</td></tr><tr><td>執行時機</td><td>每次請求</td><td>首次請求生成，之後快取，背景再生</td><td>請求時執行（類似 REST endpoint）</td></tr><tr><td>效能</td><td>中等（依賴伺服器）</td><td>快（靜態快取）</td><td>視邏輯而定</td></tr><tr><td>SEO</td><td>✅</td><td>✅</td><td>不適用（API）</td></tr><tr><td>適合場景</td><td>即時內容、個人化頁面</td><td>部落格、商品頁、新聞</td><td>驗證、表單提交、聚合 API</td></tr></tbody></table>

---

✅ **總結**

- **SSR**：即時資料，伺服器壓力大，適合動態頁面。
- **ISR**：快取靜態 + 背景更新，效能最佳，適合多內容但非即時需求。
- **API Routes**：Next.js 內建小型後端，適合輕量 API 或 BFF。

---

## 🔎 Next.js 三種模式請求流程圖

```
[ 使用者請求瀏覽器 URL ]
            │
            ▼
 ┌───────────────────────┐
 │   Next.js Router 判斷 │
 └───────────────────────┘
        │        │
        │        ├──────────────► [ /api/... 路徑 ]
        │        │                       │
        │        │                       ▼
        │        │               API Routes Handler
        │        │               (Node 端執行，回傳 JSON)
        │        │
        ▼
 [ 頁面 Route ]
        │
        ├─────────► **SSR**
        │           - 每次請求 → Node 端執行 React
        │           - 即時撈資料 → 回傳完整 HTML
        │
        ├─────────► **ISR**
        │           - 首次請求 → 生成靜態 HTML 並快取
        │           - 之後 → 直接回傳快取
        │           - 過期 (revalidate 秒數到) → 背景再生 HTML
        │
        └─────────► **SSG (純靜態)**
                    - build 時生成 HTML
                    - 所有請求都回傳同一份 HTML

```

---

## 📌 圖示解讀

- **API Routes**：
    
    
    - 請求 `/api/*` → 進入 Next.js 的 API Handler
    - 回傳 JSON 或其他格式（例如登入驗證、送表單）
- **SSR (getServerSideProps)**：
    
    
    - 每個請求都跑一次伺服端邏輯 → 最新資料
    - 缺點：伺服器壓力大
- **ISR (getStaticProps + revalidate)**：
    
    
    - 初次請求生成快取 HTML
    - 之後皆回傳快取 → 非常快
    - 到期時在背景重新生成，更新快取
- **SSG (Static Site Generation)**：
    
    
    - build 時就生成 HTML，完全靜態
    - 適合固定內容（FAQ、About Us）

---

## 🎯 使用建議

- **SSR** → 用於 **即時資料**（會員中心、儀表板、即時價格）。
- **ISR** → 用於 **高流量、定期更新內容**（新聞、部落格、商品頁）。
- **API Routes** → 適合 **輕量 API**（登入、表單處理、資料聚合）。

---