iT邦幫忙

2

Next.js - 動態路由 Dynamic Routes

  • 分享至 

  • xImage
  •  

Next.js App Router 提供了三種強大的動態路由(Dynamic Routes),讓我們可以更好的去規劃專案的架構:

  • [slug]:動態路由 (Dynamic Segment),只能匹配單一層級
  • [...slug]:捕捉全部路由 (Catch-all Segments) ,可以匹配多個層級(1層或多層),但至少要有一層
  • [[...slug]]:可選的捕捉全部路由 (Optional Catch-all),可以匹配零層或多層(連根目錄本身都能接)。

1. 單一動態路由

app/blog/[slug]/page.tsx 中括號內放單一變數名稱。它嚴格要求只能多出一個層級。

  • /blog/my-first-post → 成功 (slug = "my-first-post")
  • /blog/hello-world → 成功 (slug = "hello-world")
  • /blog → 失敗(404)
  • /blog/react/intro → 失敗(層級太多)

取得參數的方式

export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
  const { slug } = await params;
  return <div>文章 ID: {slug}</div>;
}

2. 捕捉全部路由

app/shop/[...slug]/page.tsx 使用三個點 ...。它能捕捉後面所有層級的網址,並把它們組合成一個陣列(Array)。但注意:斜線後面必須要有東西(至少要有一段)。

  • /shop/clothing → 成功 (slug = ['clothing'])
  • /shop/clothing/shirts → 成功 (slug = ['clothing', 'shirts'])
  • /shop → 失敗(因為 ... 要求至少要有一層參數)

取得參數的方式

export default async function Page({ params }: { params: Promise<{ slug: string[] }> }) {
  const { slug } = await params; // slug 是一個陣列
  return <div>目前路徑標籤: {slug.join(' > ')}</div>;
}

3. 可選的捕捉全部路由

app/docs/[[...slug]]/page.tsx 使用雙層中括號 [[...slug]]。它比上一種更彈性,連「沒有參數」的根路徑都可以一起接下來處理。

  • /docs → 成功 (slug = undefined 或不帶參數)

  • /docs/guide → 成功 (slug = ['guide'])

  • /docs/guide/getting-started → 成功 (slug = ['guide', 'getting-started'])

  • 常見應用場景:官方文件(Docs)、多層級 Wiki、或是首頁兼內頁的單一頁面渲染。

app/
└── docs/
    └── [[...slug]]/
        └── page.tsx

app/docs/[[...slug]]/page.tsx

import React from 'react';

interface DocsPageProps {
  params: Promise<{
    slug?: string[]; // 注意:因為是「可選」的,slug 可能會是 undefined
  }>;
}

export default async function DocsPage({ params }: DocsPageProps) {
  // 1. 解析非同步的 params
  const resolvedParams = await params;
  const slug = resolvedParams.slug;

  // 2. 情況 A:如果沒有 slug(即訪問 /docs 根目錄)
  if (!slug || slug.length === 0) {
    return (
      <div className="p-8">
        <h1 className="text-3xl font-bold">歡迎來到官方文件首頁</h1>
        <p className="text-gray-600 mt-2">請從左側選單選擇你想閱讀的技術指南。</p>
      </div>
    );
  }

  // 3. 情況 B:有 slug(例如 /docs/react 或 /docs/react/hooks/use-state)
  // 我們可以把陣列用斜線接回來,模擬讀取對應的檔案或路由
  const currentPath = slug.join(' / ');

  return (
    <div className="p-8">
      <h1 className="text-3xl font-bold">文件路徑:{currentPath}</h1>
      <p className="text-sm text-gray-500 mt-1">
        目前巢狀深度:{slug.length} 層
      </p>
      
      <hr className="my-4" />

      <div className="bg-gray-100 p-4 rounded-md">
        <h2 className="font-semibold text-lg">除錯資訊(Slug 陣列結構):</h2>
        <pre className="mt-2 text-sm">{JSON.stringify(slug, null, 2)}</pre>
      </div>
    </div>
  );
}

圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言