从0死磕全栈之Next.js App Router动态路由详解:从入门到实战

280 阅读3分钟

Next.js 是基于 React 的全栈框架,其动态路由功能让我们能轻松构建支持参数化 URL 的页面(比如用户详情页 /users/123、商品详情页 /products/shoes)。本文基于 Next.js App Router(app/ 目录),通过简单易懂的方式,带你掌握动态路由的核心用法。


一、动态路由的基本概念

在 Next.js 中,动态路由指的是页面路径中包含可变参数(如 idslug 等),通过这些参数可以渲染不同的内容。例如:

  • /users/[id] → 对应用户 ID 为 123... 的详情页
  • /posts/[slug] → 对应文章别名为 hello-worldnextjs-guide... 的详情页

💡 什么是 slug?
Slug 是内容的友好 URL 别名(如 nextjs-guide),用于替代生硬的 ID。例如将文章标题“如何学习 Next.js”转为 URL 路径:how-to-learn-nextjs


二、App Router 中的动态路由写法

1. 文件命名规则

app 目录下,动态参数用方括号 [ ] 作为文件夹名,页面文件必须命名为 page.tsx(或 page.js):

  • 单参数页面app/users/[id]/page.tsx → 访问 /users/123 时,id = '123'
  • 多参数页面app/posts/[category]/[slug]/page.tsx → 访问 /posts/tech/nextjs-guide 时,参数为 { category: 'tech', slug: 'nextjs-guide' }

关键区别
Pages Router 是 pages/users/[id].tsx,而 App Router 是 app/users/[id]/page.tsx


三、获取动态路由参数

在 App Router 中,动态参数通过 params 对象传入页面组件(无需 Hook):

示例:用户详情页

// app/users/[id]/page.tsx
export default async function UserDetail({ params }: { params: { id: string } }) {
  const { id } = await params; // id 类型为 string

  // 模拟从 API 获取用户数据(服务端执行!)
  const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
  const user = await res.json();

  if (!user.name) {
    return <div>用户不存在</div>;
  }

  return (
    <div style={{ padding: '2rem', fontFamily: 'sans-serif' }}>
      <h1>👤 用户详情</h1>
      <p><strong>ID:</strong> {user.id}</p>
      <p><strong>姓名:</strong> {user.name}</p>
      <p><strong>邮箱:</strong> {user.email}</p>
    </div>
  );
}

优势

  • 数据在服务端获取,自动 SSR,SEO 友好;
  • 无需 useEffect 或客户端 loading;
  • 参数直接通过函数参数传入,类型清晰。

需要注意的是,获取参数,这里用了await params,这是next15的新特性,因为next15中params是一个Promise,所以需要使用await来获取。为了兼容老版本,next15也允许不加await来访问参数,但是未来可能会弃用这种方式。


在client组件种获取参数 需要引入react的use钩子

'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>
  )
}

四、多参数动态路由示例

// app/posts/[category]/[slug]/page.tsx
export default function BlogPost({ params }: { params: { category: string; slug: string } }) {
  const { category, slug } = await params;

  return (
    <div>
      <h1>文章:{slug}</h1>
      <p>分类:{category}</p>
    </div>
  );
}

访问 /posts/tech/nextjs-guide,页面将显示:

文章:nextjs-guide
分类:tech

五、生成动态路由链接

使用 next/link<Link> 组件跳转,和 Pages Router 用法一致:

// app/page.tsx(首页)
import Link from 'next/link';

export default function HomePage() {
  const users = [
    { id: '1', name: '张三' },
    { id: '2', name: '李四' },
  ];

  return (
    <div style={{ padding: '2rem' }}>
      <h2>用户列表</h2>
      {users.map(user => (
        <div key={user.id} style={{ margin: '0.5rem 0' }}>
          <Link href={`/users/${user.id}`}>
            查看 {user.name} 的详情
          </Link>
        </div>
      ))}
    </div>
  );
}

🔗 注意<Link>href 仍使用字符串拼接,和 App Router 无关。


六、可选参数与 Catch-all 路由

1. Catch-all Segments(匹配任意深度)

  • [...slug]/page.tsx:匹配 /docs/docs/a/docs/a/b
    • /docsparams.slug = []
    • /docs/aparams.slug = ['a']
    • /docs/a/bparams.slug = ['a', 'b']
// app/docs/[...slug]/page.tsx
export default function DocsPage({ params }: { params: { slug: string[] } }) {
  const path = params.slug.join('/');
  return <p>文档路径:{path || '首页'}</p>;
}

2. Optional Catch-all(可选)

用双括号 [[...slug]] 表示参数可选(不匹配根路径):

  • app/docs/[[...slug]]/page.tsx
  • /docsslug = []
  • /docs/aslug = ['a']

七、完整实战:用户详情页(App Router)

1. 项目结构

app/
  layout.tsx          # 全局布局
  page.tsx            # 首页(用户列表)
  users/
    [id]/
      page.tsx        # 用户详情页

2. 首页:用户列表 + 跳转链接

// app/page.tsx
import Link from 'next/link';

export default async function HomePage() {
  const res = await fetch('https://jsonplaceholder.typicode.com/users');
  const users = await res.json();

  return (
    <div style={{ padding: '2rem' }}>
      <h1>👥 用户列表</h1>
      <ul>
        {users.map((user: any) => (
          <li key={user.id} style={{ margin: '0.5rem 0' }}>
            <Link href={`/users/${user.id}`} style={{ color: '#0070f3' }}>
              {user.name}
            </Link>
          </li>
        ))}
      </ul>
    </div>
  );
}

3. 用户详情页(如上文所示)

访问 /users/1,即可看到服务端渲染的用户信息,无白屏、SEO 友好!


八、总结:App Router 动态路由核心要点

项目说明
路由定义app/[param]/page.tsx
获取参数通过组件 props 的 params 对象
数据获取直接在 page.tsxawait fetch()(服务端执行)
跳转链接仍用 <Link href="/users/1">
参数类型始终为 stringstring[](Catch-all)
SEO✅ 默认 SSR,内容直接嵌入 HTML

九、为什么 App Router 更推荐?

  • 🚀 更简洁:无需 getServerSideProps,数据获取更直观
  • 🔒 更安全:服务端组件默认,敏感逻辑不暴露给客户端
  • 🌐 SEO 友好:HTML 直出内容,搜索引擎轻松抓取
  • 🧩 组合灵活:配合 layout.tsxloading.tsxerror.tsx 实现完整体验

掌握 App Router 的动态路由,你就能轻松构建博客、电商、用户中心等现代 Web 应用!🚀