Skip to main content

Next.js 路由機制

Next.js 提供了多種路由方式,適用於不同場景。本回答將涵蓋:

  1. Pages Router:基於 pages 資料夾的傳統路由系統。

  2. App Router:Next.js 13+ 引入的進階路由系統,基於 app 資料夾與 React Server Components。

  3. Dynamic Routes:處理動態參數的路由,適用於 Pages Router 和 App Router。

  4. Intercepting Routes:攔截特定路由以顯示模態視窗或覆蓋內容。

  5. Parallel Routes:同時渲染多個獨立內容區域,適用於複雜佈局。


1. Pages Router

Pages Router 是 Next.js 早期的路由系統,基於檔案系統,位於 pages 資料夾的檔案直接對應 URL 路徑。雖然簡單,但功能較有限,Next.js 13+ 更推薦使用 App Router。

特色:

  • 每個 .js/.jsx 檔案對應一個路由,例如 pages/about.js 對應 /about。

  • 支援動態路由,透過 [param].js 處理動態參數。

  • 適合小型專案或快速原型開發。

程式碼範例:

檔案結構:

my-next-app/
├── pages/
│ ├── index.js
│ ├── about.js
│ └── post/
│ └── [id].js

pages/index.js(首頁):

// pages/index.js
import React from "react";

export default function Home() {
return (
<div>
<h1>歡迎來到我的 Next.js 應用程式!</h1>
<p>這是首頁</p>
</div>
);
}

pages/about.js(關於頁面):

// pages/about.js
import React from "react";

export default function About() {
return (
<div>
<h1>關於我們</h1>
<p>這是關於頁面</p>
</div>
);
}

pages/post/[id].js(動態路由):

// pages/post/[id].js
import React from "react";
import { useRouter } from "next/router";

export default function Post() {
const router = useRouter();
const { id } = router.query;

return (
<div>
<h1>文章頁面</h1>
<p>文章 ID: {id}</p>
</div>
);
}

執行結果:

優點與缺點:

  • 優點:簡單直觀,適合初學者。

  • 缺點:功能有限,與新功能(如伺服器組件)相容性差。


2. App Router

App Router 是 Next.js 13+ 引入的路由系統,基於 app 資料夾與 React Server Components,支援進階功能如伺服器端渲染、佈局和資料獲取。

特色:

  • 每個資料夾對應一個路由段,page.js 定義頁面內容。

  • 支援 layout.js(共用佈局)、loading.js(載入狀態)等特殊檔案。

  • 支援進階路由功能,如攔截路由和平行路由。

程式碼範例:

檔案結構:

my-next-app/
├── app/
│ ├── layout.js
│ ├── page.js
│ └── about/
│ └── page.js

app/layout.js(共用佈局):

// app/layout.js
export default function RootLayout({ children }) {
return (
<html lang="zh-TW">
<body>
<header>
<h1>我的 Next.js 應用程式</h1>
<nav>
<a href="/">首頁</a> | <a href="/about">關於</a>
</nav>
</header>
{children}
</body>
</html>
);
}

app/page.js(首頁):

// app/page.js
export default function Home() {
return (
<div>
<h1>歡迎來到首頁!</h1>
<p>這是使用 App Router 的首頁</p>
</div>
);
}

app/about/page.js(關於頁面):

// app/about/page.js
export default function About() {
return (
<div>
<h1>關於我們</h1>
<p>這是使用 App Router 的關於頁面</p>
</div>
);
}

執行結果:

優點與缺點:

  • 優點:支援伺服器組件、進階功能,效能更好。

  • 缺點:學習曲線較高,與 Pages Router 不相容。


3. Dynamic Routes(動態路由)

Dynamic Routes 是 App Router(和 Pages Router)中處理動態參數的路由方式,透過 [param] 或 [...param] 定義動態路徑。這裡專注於 App Router 的實現,並包含進階用法。

特色:

  • [param]:單一動態參數,例如 /post/1。

  • [...param]:捕捉多層路徑(Catch-all Routes),例如 /post/2023/10/01。

  • [[...param]]:可選 Catch-all Routes,允許路徑為空。

程式碼範例:

檔案結構:

my-next-app/
├── app/
│ ├── post/
│ │ ├── [id]/
│ │ │ └── page.js
│ │ └── [...slug]/
│ │ └── page.js
│ └── layout.js

app/post/[id]/page.js(單一動態參數):

// app/post/[id]/page.js
export default function Post({ params }) {
const { id } = params;

return (
<div>
<h1>文章頁面</h1>
<p>文章 ID: {id}</p>
</div>
);
}

app/post/[...slug]/page.js(捕捉多層路徑):

// app/post/[...slug]/page.js
export default function PostSlug({ params }) {
const { slug } = params;

return (
<div>
<h1>多層路徑文章</h1>
<p>路徑: {slug ? slug.join("/") : "無路徑"}</p>
</div>
);
}

app/post/[[...slug]]/page.js(可選 Catch-all Routes,可選):

// app/post/[[...slug]]/page.js
export default function PostOptionalSlug({ params }) {
const { slug } = params;

return (
<div>
<h1>可選路徑文章</h1>
<p>路徑: {slug ? slug.join("/") : "無路徑"}</p>
</div>
);
}

執行結果:

優點與缺點:

  • 優點:靈活處理動態參數,適用於博客、電子商務等場景。

  • 缺點:需自行處理參數邏輯,可能增加複雜度。


4. Intercepting Routes(攔截路由)

Intercepting Routes 是 App Router 的進階功能,允許在不改變 URL 的情況下攔截特定路由並渲染不同內容,常用于模態視窗或覆蓋式頁面。

特色:

  • 使用 (.) 攔截同層級路由,(..) 攔截上一層級路由。

  • 常與客戶端導航(Link 或 useRouter)結合,提供模態式體驗。

程式碼範例:

假設你有一個博客網站,/posts 顯示文章列表,點擊文章時以模態視窗顯示詳情。

檔案結構:

my-next-app/
├── app/
│ ├── posts/
│ │ ├── (.)post/
│ │ │ └── [id]/
│ │ │ └── page.js
│ │ └── page.js
│ └── layout.js

app/posts/page.js(文章列表):

// app/posts/page.js
import Link from "next/link";

export default function Posts() {
const posts = [
{ id: 1, title: "第一篇文章" },
{ id: 2, title: "第二篇文章" },
];

return (
<div>
<h1>文章列表</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>
<Link href={`/post/${post.id}`}>{post.title}</Link>
</li>
))}
</ul>
</div>
);
}

app/posts/(.)post/[id]/page.js(攔截文章詳情):

// app/posts/(.)post/[id]/page.js
import Link from 'next/link';

export default function PostModal({ params })26: { id } = params;

return (
<div
style={{
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
background: 'white',
padding: '20px',
border: '1px solid #ccc',
zIndex: 1000,
}}
>
<h1>文章詳情 (模態視窗)</h1>
<p>文章 ID: {id}</p>
<Link href="/posts">關閉</Link>
</div>
);
}

執行結果:

  • 訪問 http://localhost:3000/posts 顯示文章列表。

  • 點擊「第一篇文章」,URL 顯示為 /post/1,但頁面顯示模態視窗(文章詳情)。

  • 點擊「關閉」返回文章列表。

優點與缺點:

  • 優點:無需改變 URL,提供模態式體驗。

  • 缺點:設定較複雜,需熟悉路由匹配邏輯。


5. Parallel Routes(平行路由)

Parallel Routes 是 App Router 的進階功能,透過 @folder 命名資料夾,允許在同一路由下同時渲染多個獨立內容區域,適合複雜佈局如儀表板。

特色:

  • 每個 @folder 定義一個獨立內容區域,透過 layout.js 的插槽(slot)組合。

  • 支援獨立的錯誤處理和載入狀態。

  • 可與動態路由結合。

程式碼範例:

假設你正在構建一個儀表板,包含側邊欄(@sidebar)和主內容(@main)。

檔案結構:

my-next-app/
├── app/
│ ├── dashboard/
│ │ ├── @sidebar/
│ │ │ └── page.js
│ │ ├── @main/
│ │ │ └── [userId]/
│ │ │ └── page.js
│ │ └── layout.js
│ └── layout.js

app/dashboard/layout.js(平行路由佈局):

// app/dashboard/layout.js
export default function DashboardLayout({ children, sidebar, main }) {
return (
<div style={{ display: "flex" }}>
<div style={{ width: "200px", background: "#f0f0f0", padding: "20px" }}>
{sidebar}
</div>
<div style={{ flex: 1, padding: "20px" }}>{main}</div>
{children}
</div>
);
}

app/dashboard/@sidebar/page.js(側邊欄):

// app/dashboard/@sidebar/page.js
export default function Sidebar() {
return (
<div>
<h2>側邊欄</h2>
<ul>
<li>
<a href="/dashboard">首頁</a>
</li>
<li>
<a href="/dashboard/settings">設定</a>
</li>
</ul>
</div>
);
}

app/dashboard/@main/[userId]/page.js(主內容,結合動態路由):

// app/dashboard/@main/[userId]/page.js
export default function UserMain({ params }) {
const { userId } = params;

return (
<div>
<h2>使用者主內容</h2>
<p>使用者 ID: {userId}</p>
</div>
);
}

執行結果:

  • 訪問 http://localhost:3000/dashboard/123 顯示儀表板,側邊欄顯示導航選單,主內容顯示「使用者 ID: 123」。

  • children(page.js)可作為額外內容區域,若無需可省略。

優點與缺點:

  • 優點:支援複雜佈局,模組化設計。

  • 缺點:需設計清晰的插槽結構,初學者可能較難上手。


總結比較

路由方式用途優點缺點
Pages Router小型專案、快速原型簡單直觀,易上手功能有限,與新功能相容性差
App Router進階應用、伺服器組件支援伺服器組件、進階功能,效能更好學習曲線較高,需調整舊程式碼
Dynamic Routes處理動態參數與多層路徑靈活,支援單一與多層參數需自行處理參數邏輯
Intercepting Routes攔截路由以顯示模態視窗或覆蓋內容無需改變 URL,模態式體驗設定複雜,需熟悉路由匹配
Parallel Routes同時渲染多個獨立內容區域支援複雜佈局,模組化需清晰的插槽結構,初學者較難上手

實作建議與操作步驟

  1. 環境設定

    • 安裝 Next.js:npx create-next-app@latest my-next-app

    • 進入專案:cd my-next-app

    • 啟動開發伺服器:npm run dev

  2. 選擇路由方式

    • 初學者:從 Pages Router 開始,熟悉檔案對應路由的簡單邏輯。

    • 進階需求:使用 App Router,結合伺服器組件與進階功能。

    • 動態參數:根據需求選擇 [param] 或 [...param],並妥善處理 params。

    • 模態視窗:使用 Intercepting Routes,結合 Link 實現無縫導航。

    • 複雜佈局:使用 Parallel Routes,設計清晰的 @folder 結構。

  3. 測試與除錯

    • http://localhost:3000 測試各路由是否正確渲染。

    • 使用 console.log(params) 檢查動態參數是否正確傳遞。

    • 確保 layout.js 正確組合平行路由的插槽。

常見問題與解決方法

  1. 路由未正確顯示

    • 檢查檔案名稱是否正確(例如 [id] 而非 (id))。

    • 確保 layout.js 正確傳遞 children 或插槽。

  2. 動態參數未傳遞

    • 在 page.js 中使用 console.log(params) 檢查參數。

    • 確認資料夾名稱使用 [param] 或 [...param]。

  3. 模態視窗樣式異常

    • 檢查 style 屬性,確保 position: fixed 和 zIndex 設定正確。
  4. 平行路由未渲染

    • 確認 layout.js 正確定義插槽(例如 sidebar、main)。