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

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):

示例:用户详情页

tsx 复制代码
// 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钩子

javascript 复制代码
'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>
  )
}

四、多参数动态路由示例

tsx 复制代码
// 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 用法一致:

tsx 复制代码
// 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']
tsx 复制代码
// 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. 项目结构

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

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

tsx 复制代码
// 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 应用!🚀


相关推荐
huangql5202 小时前
基于前端+Node.js 的 Markdown 笔记 PDF 导出系统完整实战
前端·笔记·node.js
在逃的吗喽2 小时前
Vue3新变化
前端·javascript·vue.js
yqwang_cn2 小时前
打造优雅的用户体验:自定义jQuery工具提示插件开发全解析
前端·jquery·ux
小Tomkk2 小时前
AI 提效:利用 AI 从前端 快速转型为UI/UX设计师和产品
前端·人工智能·ui
Demoncode_y3 小时前
Vue3中基于路由的动态递归菜单组件实现
前端·javascript·vue.js·学习·递归·菜单组件
杨超越luckly3 小时前
HTML应用指南:利用POST请求获取全国中国工商农业银行网点位置信息
大数据·前端·html·数据可视化·银行网点
皮蛋瘦肉粥_1213 小时前
pink老师html5+css3day02
前端·css3·html5
qianmo20214 小时前
基于pycharm实现html文件的快速实现问题讨论
前端·html
IT_陈寒4 小时前
SpringBoot3踩坑实录:一个@Async注解让我多扛了5000QPS
前端·人工智能·后端