Skip to main content

动态路由

当你事先不知道确切的路段名称并希望从动态数据创建路由时,你可以使用在请求时填充的动态路段或在构建时填充的 prerendered

¥When you don't know the exact segment names ahead of time and want to create routes from dynamic data, you can use Dynamic Segments that are filled in at request time or prerendered at build time.

惯例

¥Convention

可以通过将文件夹名称括在方括号中来创建动态段:[folderName]。例如,[id][slug]

¥A Dynamic Segment can be created by wrapping a folder's name in square brackets: [folderName]. For example, [id] or [slug].

动态段作为 params 属性传递给 layoutpageroutegenerateMetadata 函数。

¥Dynamic Segments are passed as the params prop to layout, page, route, and generateMetadata functions.

示例

¥Example

例如,博客可以包含以下路由 app/blog/[slug]/page.js,其中 [slug] 是博客帖子的动态分段。

¥For example, a blog could include the following route app/blog/[slug]/page.js where [slug] is the Dynamic Segment for blog posts.

export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const slug = (await params).slug
return <div>My Post: {slug}</div>
}
export default async function Page({ params }) {
const slug = (await params).slug
return <div>My Post: {slug}</div>
}
路由示例网址params
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' }

请参阅 generateStaticParams() 页面以了解如何生成该段的参数。

¥See the generateStaticParams() page to learn how to generate the params for the segment.

很高兴知道

¥Good to know

  • 由于 params prop 是一个 promise。你必须使用 async/await 或 React 的使用函数来访问值。

    ¥Since the params prop is a promise. You must use async/await or React's use function to access the values.

    • 在版本 14 及更早版本中,params 是一个同步 prop。为了帮助向后兼容,你仍然可以在 Next.js 15 中同步访问它,但此行为将来会被弃用。

      ¥In version 14 and earlier, params was a synchronous prop. To help with backwards compatibility, you can still access it synchronously in Next.js 15, but this behavior will be deprecated in the future.

  • 动态段相当于 pages 目录中的 动态路由

    ¥Dynamic Segments are equivalent to Dynamic Routes in the pages directory.

生成静态参数

¥Generating Static Params

generateStaticParams 功能可以在构建时与 动态路由段静态生成 路由结合使用,而不是在请求时按需使用。

¥The generateStaticParams function can be used in combination with dynamic route segments to statically generate routes at build time instead of on-demand at request time.

export async function generateStaticParams() {
const posts = await fetch('https://.../posts').then((res) => res.json())

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

return posts.map((post) => ({
slug: post.slug,
}))
}

generateStaticParams 功能的主要优点是智能检索数据。如果使用 fetch 请求在 generateStaticParams 函数内获取内容,则请求为 自动记忆。这意味着跨多个 generateStaticParams、布局和页面具有相同参数的 fetch 请求只会发出一次,从而减少构建时间。

¥The primary benefit of the generateStaticParams function is its smart retrieval of data. If content is fetched within the generateStaticParams function using a fetch request, the requests are automatically memoized. This means a fetch request with the same arguments across multiple generateStaticParams, Layouts, and Pages will only be made once, which decreases build times.

如果你要从 pages 目录迁移,请使用 迁移指南

¥Use the migration guide if you are migrating from the pages directory.

请参阅 generateStaticParams 服务器功能文档 了解更多信息和高级用例。

¥See generateStaticParams server function documentation for more information and advanced use cases.

捕获所有片段

¥Catch-all Segments

通过在括号 [...folderName] 内添加省略号,可以扩展动态段以捕获所有后续段。

¥Dynamic Segments can be extended to catch-all subsequent segments by adding an ellipsis inside the brackets [...folderName].

例如,app/shop/[...slug]/page.js 将匹配 /shop/clothes,但也会匹配 /shop/clothes/tops/shop/clothes/tops/t-shirts 等。

¥For example, app/shop/[...slug]/page.js will match /shop/clothes, but also /shop/clothes/tops, /shop/clothes/tops/t-shirts, and so on.

路由示例网址params
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'] }

可选的包罗万象的段

¥Optional Catch-all Segments

通过将参数包含在双方括号中,可以使包罗万象的段成为可选:[[...folderName]]

¥Catch-all Segments can be made optional by including the parameter in double square brackets: [[...folderName]].

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

¥For example, app/shop/[[...slug]]/page.js will also match /shop, in addition to /shop/clothes, /shop/clothes/tops, /shop/clothes/tops/t-shirts.

catch-all 和可选的 catch-all 段之间的区别在于,使用可选时,不带参数的路由也会被匹配(上例中的 /shop)。

¥The difference between catch-all and optional catch-all segments is that with optional, the route without the parameter is also matched (/shop in the example above).

路由示例网址params
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 的类型。

¥When using TypeScript, you can add types for params depending on your configured route segment.

export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
return <h1>My Page</h1>
}
export default async function Page({ params }) {
return <h1>My Page</h1>
}
路由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 }

很高兴知道:将来这可能会由 TypeScript 插件 自动补齐。

¥Good to know: This may be done automatically by the TypeScript plugin in the future.