⚠️ Alpha内测版本警告:此为早期内部构建版本,尚不完整且可能存在错误,欢迎大家提Issue反馈问题或建议
Skip to content

3.1.2 如何给成千上万的页面做路由——动态路由

一句话破题

用方括号 [param] 命名文件夹,即可创建动态路由,一套代码服务无限页面。

核心价值

博客有上千篇文章,商城有上万个商品,用户有数百万个主页——你不可能为每个实体手动创建一个 page.tsx。动态路由让一个页面模板匹配无限多的 URL 路径。

动态路由语法

语法示例路径匹配 URLparams 值
[slug]app/blog/[slug]/page.tsx/blog/hello-world{ slug: 'hello-world' }
[...slug]app/docs/[...slug]/page.tsx/docs/a/b/c{ slug: ['a', 'b', 'c'] }
[[...slug]]app/shop/[[...slug]]/page.tsx/shop/shop/a/b{ slug: undefined }{ slug: ['a', 'b'] }

快速上手

基础动态路由:

app/
└── blog/
    └── [slug]/
        └── page.tsx    # 匹配 /blog/任意内容

获取动态参数:

tsx
// app/blog/[slug]/page.tsx
type Props = {
  params: { slug: string }
}

export default async function BlogPost({ params }: Props) {
  const { slug } = params
  
  // 根据 slug 获取文章数据
  const post = await getPostBySlug(slug)
  
  if (!post) {
    notFound() // 触发 not-found.tsx
  }
  
  return (
    <article>
      <h1>{post.title}</h1>
      <div>{post.content}</div>
    </article>
  )
}

Catch-all 路由

当你需要匹配多级路径时,使用 [...param] 语法:

tsx
// app/docs/[...slug]/page.tsx
// 匹配 /docs/getting-started
// 匹配 /docs/api/authentication/oauth

type Props = {
  params: { slug: string[] }
}

export default function DocsPage({ params }: Props) {
  // slug 是数组:['api', 'authentication', 'oauth']
  const breadcrumb = params.slug.join(' > ')
  
  return <div>当前路径:{breadcrumb}</div>
}

静态生成动态路由

对于 SEO 关键页面,可以在构建时预生成所有可能的路径:

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

// 告诉 Next.js 预先生成哪些路径
export async function generateStaticParams() {
  const posts = await getAllPosts()
  
  return posts.map((post) => ({
    slug: post.slug,
  }))
}

// 页面组件保持不变
export default async function BlogPost({ params }: Props) {
  // ...
}

搜索参数 vs 路由参数

除了路由参数,还可以通过 URL 查询字符串传递数据:

tsx
// URL: /products?category=electronics&sort=price

type Props = {
  params: { id: string }
  searchParams: { category?: string; sort?: string }
}

export default function ProductsPage({ searchParams }: Props) {
  const { category, sort } = searchParams
  
  return (
    <div>
      <p>分类: {category}</p>
      <p>排序: {sort}</p>
    </div>
  )
}
类型用途示例
路由参数标识资源/blog/my-post(文章唯一标识)
搜索参数筛选/排序/products?sort=price(列表筛选条件)

AI 协作指南

核心意图:让 AI 帮你创建支持动态内容的页面。

需求定义公式

  • 功能描述:我需要一个 [资源类型] 的详情页
  • 交互方式:URL 格式为 /[资源名]/[标识符]
  • 预期效果:根据 URL 参数获取并展示对应数据

关键术语[slug]paramsgenerateStaticParamsnotFoundsearchParams

交互策略

  1. 先让 AI 创建动态路由的文件结构
  2. 让它实现参数获取和数据请求逻辑
  3. 补充 404 处理和 loading 状态
  4. 如需 SSG,让它添加 generateStaticParams

避坑指南

  1. 动态参数是字符串:即使 URL 是 /post/123params.id 也是 "123" 而非 123
  2. catch-all 参数是数组[...slug] 返回的是 string[],记得处理空数组情况
  3. generateStaticParams 返回值必须是对象数组[{ slug: 'a' }, { slug: 'b' }]
  4. 动态路由默认是动态渲染:除非使用 generateStaticParams 预生成

验收清单

  • [ ] 动态路由能正确匹配不同的 URL 路径
  • [ ] params 能正确获取到 URL 中的动态部分
  • [ ] 无效的动态参数能触发 404 页面
  • [ ] 需要 SEO 的页面已配置 generateStaticParams