从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 应用!🚀


相关推荐
李少兄1 小时前
HTML 表单控件
前端·microsoft·html
学习笔记1013 小时前
第十五章认识Ajax(六)
前端·javascript·ajax
消失的旧时光-19433 小时前
Flutter 异步编程:Future 与 Stream 深度解析
android·前端·flutter
曹牧3 小时前
C# 中的 DateTime.Now.ToString() 方法支持多种预定义的格式字符
前端·c#
勿在浮沙筑高台3 小时前
海龟交易系统R
前端·人工智能·r语言
歪歪1003 小时前
C#如何在数据可视化工具中进行数据筛选?
开发语言·前端·信息可视化·前端框架·c#·visual studio
Captaincc4 小时前
AI 能帮你写代码,但把代码变成软件,还是得靠人
前端·后端·程序员
吃饺子不吃馅5 小时前
如何设计一个 Canvas 事件系统?
前端·canvas·图形学
Baklib梅梅6 小时前
无头内容管理系统:打造灵活高效的多渠道内容架构
前端·ruby on rails·前端框架·ruby
over6976 小时前
浏览器里的AI魔法:用JavaScript玩转自然语言处理
前端·javascript