跳到內容

動態路由段

當您事先不知道確切的路由段名稱,並希望從動態資料建立路由時,可以使用動態路由段,這些路由段可以在請求時填充或在構建時預渲染。

約定

動態路由段可以透過將資料夾名稱用方括號包裹來建立:[folderName]。例如,一個部落格可以包含以下路由 app/blog/[slug]/page.js,其中 [slug] 是部落格文章的動態路由段。

app/blog/[slug]/page.tsx
export default async function Page({
  params,
}: {
  params: Promise<{ slug: string }>
}) {
  const { slug } = await params
  return <div>My Post: {slug}</div>
}

動態路由段作為 params 屬性傳遞給 layoutpageroutegenerateMetadata 函式。

路由示例 URLparams
app/blog/[slug]/page.js/blog/a{ slug: 'a' }
app/blog/[slug]/page.js/blog/b{ slug: 'b' }
app/blog/[slug]/page.js/blog/c{ slug: 'c' }

在客戶端元件中

在客戶端元件的 page 中,可以使用 use 鉤子訪問來自 props 的動態路由段。

app/blog/[slug]/page.tsx
'use client'
import { use } from 'react'
 
export default function BlogPostPage({
  params,
}: {
  params: Promise<{ slug: string }>
}) {
  const { slug } = use(params)
 
  return (
    <div>
      <p>{slug}</p>
    </div>
  )
}

或者,客戶端元件可以使用 useParams 鉤子在客戶端元件樹中的任何位置訪問 params

捕獲所有路由段

動態路由段可以透過在方括號內新增省略號 [...folderName] 來擴充套件,以 捕獲所有 後續路由段。

例如,app/shop/[...slug]/page.js 將匹配 /shop/clothes,以及 /shop/clothes/tops/shop/clothes/tops/t-shirts 等。

路由示例 URLparams
app/shop/[...slug]/page.js/shop/a{ slug: ['a'] }
app/shop/[...slug]/page.js/shop/a/b{ slug: ['a', 'b'] }
app/shop/[...slug]/page.js/shop/a/b/c{ slug: ['a', 'b', 'c'] }

可選的捕獲所有路由段

捕獲所有路由段可以透過在雙層方括號 [[...folderName]] 中包含引數來使其 可選

例如,app/shop/[[...slug]]/page.js 除了匹配 /shop/clothes/shop/clothes/tops/shop/clothes/tops/t-shirts 之外, 將匹配 /shop

捕獲所有可選捕獲所有 路由段之間的區別在於,可選的路由段也匹配不帶引數的路由(在上述示例中為 /shop)。

路由示例 URLparams
app/shop/[[...slug]]/page.js/shop{ slug: undefined }
app/shop/[[...slug]]/page.js/shop/a{ slug: ['a'] }
app/shop/[[...slug]]/page.js/shop/a/b{ slug: ['a', 'b'] }
app/shop/[[...slug]]/page.js/shop/a/b/c{ slug: ['a', 'b', 'c'] }

TypeScript

使用 TypeScript 時,您可以根據您配置的路由段新增 params 的型別 — 分別使用 PageProps<'/route'>LayoutProps<'/route'>RouteContext<'/route'> 來為 pagelayoutroute 中的 params 定義型別。

路由 params 的值被型別化為 stringstring[]undefined(對於可選的捕獲所有路由段),因為它們的值在執行時之前是未知的。使用者可以在位址列中輸入任何 URL,這些寬泛的型別有助於確保您的應用程式程式碼處理所有這些可能的情況。

路由params 型別定義
app/blog/[slug]/page.js{ slug: string }
app/shop/[...slug]/page.js{ slug: string[] }
app/shop/[[...slug]]/page.js{ slug?: string[] }
app/[categoryId]/[itemId]/page.js{ categoryId: string, itemId: string }

如果您正在處理一個 params 只能有固定數量有效值的路由,例如具有已知語言程式碼集的 [locale] 引數,您可以使用執行時驗證來處理使用者可能輸入的任何無效引數,並讓您的應用程式的其餘部分使用您已知集中的更窄的型別。

/app/[locale]/page.tsx
import { notFound } from 'next/navigation'
import type { Locale } from '@i18n/types'
import { isValidLocale } from '@i18n/utils'
 
function assertValidLocale(value: string): asserts value is Locale {
  if (!isValidLocale(value)) notFound()
}
 
export default async function Page(props: PageProps<'/[locale]'>) {
  const { locale } = await props.params // locale is typed as string
  assertValidLocale(locale)
  // locale is now typed as Locale
}

行為

  • 由於 params 屬性是一個 Promise。您必須使用 async/await 或 React 的 use 函式來訪問這些值。
    • 在 14 版及更早版本中,params 是一個同步屬性。為了幫助實現向後相容性,您仍然可以在 Next.js 15 中同步訪問它,但此行為將來會被棄用。

示例

使用 generateStaticParams

generateStaticParams 函式可用於在構建時(而不是按需請求時)靜態生成 路由。

app/blog/[slug]/page.tsx
export async function generateStaticParams() {
  const posts = await fetch('https://.../posts').then((res) => res.json())
 
  return posts.map((post) => ({
    slug: post.slug,
  }))
}

generateStaticParams 函式內部使用 fetch 時,請求會 自動去重。這可以避免佈局、頁面和其他 generateStaticParams 函式對相同資料進行多次網路呼叫,從而加快構建時間。

下一步

有關下一步操作的更多資訊,我們推薦以下部分