跳到主要内容

Next.js 路由体系

Next.js 的路由系统不是单纯把文件映射成路径,它还顺带定义了页面层级、布局继承、加载状态和一部分服务端边界。

Pages Router 的基础路由

普通页面

pages/index.tsx -> /
pages/about.tsx -> /about
pages/posts/list.tsx -> /posts/list

动态路由

pages/posts/[id].tsx -> /posts/1

Catch-all 路由

pages/docs/[...slug].tsx

它可以匹配:

  • /docs/a
  • /docs/a/b
  • /docs/a/b/c

Optional Catch-all

pages/docs/[[...slug]].tsx

它连 /docs 本身也能匹配。

App Router 的路由系统

到了 App Router,路由不只是一张 URL 映射表,而是一套“路由段结构”。

页面入口

app/page.tsx -> /
app/about/page.tsx -> /about
app/blog/[slug]/page.tsx -> /blog/hello

动态参数

export default async function BlogPost({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
return <h1>{slug}</h1>;
}

在较新的版本里,paramssearchParams 需要按异步模型理解。

布局为什么会改变路由体验

layout.tsx 是 App Router 最值得重视的能力之一。

app/
layout.tsx
dashboard/
layout.tsx
page.tsx
settings/
page.tsx

这意味着:

  • 根布局包住全站
  • dashboard/layout.tsx 只包住 dashboard 这一路径下的页面
  • 路由切换时,共享布局部分可以被复用,不需要整页重新挂载

这也是 App Router 体验和传统 React Router 不太一样的原因之一。

路由组

app/(marketing)/about/page.tsx
app/(dashboard)/settings/page.tsx

路由组不会进入 URL。它的作用是:

  • 整理目录
  • 区分布局
  • 在不改变地址结构的前提下划分区域

并行路由

app/@team/page.tsx
app/@analytics/page.tsx

并行路由适合多区域同时渲染的页面,例如:

  • 仪表盘
  • 多面板后台
  • 一个主页面带多个内容槽位

它不是高频入门能力,但在大型后台里很常见。

拦截路由

拦截路由常见于:

  • 列表页中打开详情弹层
  • 地址变化,但底层页面保持上下文
  • 模态详情和独立详情页共用同一路由资源

官方给出的文件约定比较特殊,例如 (..)(...) 这类写法,第一次看会有些绕,建议在需要做 modal routing 时再专门深挖。

导航方式

import Link from 'next/link';

export default function Nav() {
return <Link href="/dashboard">Dashboard</Link>;
}

这是默认首选方式。它会接入 Next 的预取和客户端导航能力。

编程式导航

'use client';

import { useRouter } from 'next/navigation';

export default function Actions() {
const router = useRouter();

return <button onClick={() => router.push('/orders')}>Orders</button>;
}

读取当前路径

'use client';

import { usePathname, useSearchParams } from 'next/navigation';

export default function CurrentRoute() {
const pathname = usePathname();
const searchParams = useSearchParams();

return (
<div>
<div>{pathname}</div>
<div>{searchParams.get('q')}</div>
</div>
);
}

Route Handlers

App Router 下,接口层也接进了文件系统路由:

app/api/posts/route.ts
export async function GET() {
return Response.json([{ id: 1, title: 'Hello' }]);
}

这让前端项目在很多轻量 BFF 场景下不需要马上拆独立服务。

路由系统最常见的误区

1. 把 URL 结构和目录结构完全绑定死

Next 的文件系统路由是主线,但路由组、并行路由、拦截路由都在说明:目录结构仍然有“组织层”和“URL 层”的区别。

2. 低估 layout.tsx 的作用

布局复用不只是省代码,它会直接影响页面切换体验和组件重挂载行为。

3. 把 Route Handlers 当成完整后端替代品

做轻量接口可以,做重服务端系统则要谨慎。

App Router 和 Pages Router 怎么看

很多 Next 文档最容易让人混乱的地方,不是 API,而是两套路由系统并存。

当前可以这样理解:

  • Pages Router:旧主线,成熟、稳定、存量项目非常多
  • App Router:当前主线,围绕 Server Components、布局复用、缓存和流式渲染重建了一套模型

这两套不是谁“彻底不能用”,而是侧重点已经明显不同。

Pages Router 是什么

Pages Router 就是大家更熟悉的这一套:

  • pages/index.tsx
  • pages/posts/[id].tsx
  • getStaticProps
  • getServerSideProps
  • pages/api/*
export async function getServerSideProps() {
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();

return {
props: { posts },
};
}

它的优点很明确:

  • 心智模型简单
  • React 团队容易上手
  • 大量历史资料和实践都围绕这套写法

App Router 是什么

App Router 对应 app/ 目录。它不是把原路由系统改个壳,而是把几个基础能力一起重写了:

  • 布局是一级能力
  • Server Component 是默认状态
  • 数据获取和页面渲染更紧密
  • 缓存、重验证、流式渲染都是内建模型的一部分
export default async function Page() {
const res = await fetch('https://api.example.com/posts');
const posts = await res.json();

return <PostList posts={posts} />;
}

两套最大的差异不在“文件名”,而在心智模型

Pages Router 的核心问题

  • 页面在哪
  • 这个页面用 SSG、SSR 还是 CSR
  • 数据通过哪个生命周期函数拿

App Router 的核心问题

  • 这个路由段的布局怎么组织
  • 这段 UI 该在服务端还是客户端执行
  • 这段数据该缓存多久,怎么失效
  • 是否需要流式渲染

所以 App Router 并不只是新语法,而是问题意识变了。

一张表看差异

维度Pages RouterApp Router
目录入口pages/app/
页面主文件pages/*.tsxpage.tsx
共享布局_app.tsx / 组件拼装layout.tsx
数据获取getStaticProps / getServerSideProps组件里直接 fetch
组件默认位置客户端 React 组件Server Component 默认
API 层pages/api/*route.ts
缓存模型较少显式暴露缓存与重验证是核心模型
推荐定位存量维护、渐进迁移新项目主线

什么时候还会继续用 Pages Router

这些场景很常见:

  • 老项目已经稳定跑了多年
  • 团队对 getServerSideProps 很熟
  • 迁移成本大于短期收益
  • 项目不打算引入 Server Components 和新的缓存模型

这类项目继续维护 Pages Router 完全正常。

什么时候更适合 App Router

更适合这些场景:

  • 新项目
  • 需要更细的布局复用
  • 想减少客户端 JS
  • 需要把服务端数据获取和页面渲染贴得更近
  • 准备接受新一代缓存和重验证模型

两套能不能并存

可以。

官方就支持在一个项目里同时存在 app/pages/。迁移时很常见的做法是:

  • 新页面走 app/
  • 旧页面先留在 pages/
  • 公共组件和公共逻辑抽到共享目录

迁移时最容易误判的地方

1. 以为 App Router 只是换了目录

其实不是。Server / Client 边界、缓存、布局、错误处理、路由段行为都会一起变化。

2. 以为所有页面都应该立刻迁完

现实项目里,分批迁移通常更稳。

3. 以为有 App Router 就完全不需要了解 Pages Router

也不对。只要会接存量项目,Pages Router 依然是基础能力。

推荐做法

如果现在开始补 Next 基础,比较稳的顺序通常是:

  1. 先理解 Pages Router 基本模型
  2. 再理解 App Router 为什么重新设计
  3. 最后看迁移、缓存和 写操作与接口层

这样更容易把新旧经验接起来。

参考资料

推荐继续往下看

  1. 项目结构
  2. 渲染、数据获取与水合
  3. 写操作与接口层

参考资料