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


相关推荐
毕设源码-朱学姐27 分钟前
【开题答辩全过程】以 基于vue.js的校园二手平台为例,包含答辩的问题和答案
前端·javascript·vue.js
m0_4711996341 分钟前
【JavaScript】Set 和 Map 核心区别与实战用法(ES6 集合全解析)
前端·javascript·es6
hoiii1871 小时前
MATLAB中主成分分析(PCA)与相关性分析的实现
前端·人工智能·matlab
大波V51 小时前
用 nvm 彻底重装 Node 12.22.12(确保干净)
前端
和和和2 小时前
React Scheduler为何采用MessageChannel调度?
前端·javascript
Ric9702 小时前
Mac上Git不识别文件名大小写修改?一招搞定!
前端
和和和2 小时前
前端应该知道的浏览器知识
前端·javascript
狗哥哥2 小时前
前端基础数据中心:从混乱到统一的架构演进
前端·vue.js·架构
树深遇鹿2 小时前
数据字典技术方案实战
前端·javascript·架构
大布布将军2 小时前
一种名为“Webpack 配置工程师”的已故职业—— Vite 与“零配置”的快乐
前端·javascript·学习·程序人生·webpack·前端框架·学习方法