Next.js 是目前 React 生态中最成熟的全栈框架,它基于 React 构建,但提供了路由、数据获取、渲染策略、API 路由等一站式解决方案。对于需要 SEO、首屏性能优化或复杂后端逻辑的 React 应用,Next.js 几乎是首选方案。
路由系统
Next.js 的路由基于文件系统,即 pages 或 app 目录下的文件结构直接映射为应用的路由。这种约定大于配置的设计让路由组织一目了然,无需额外安装路由库。
Pages Router(传统路由)
在 pages 目录下,每个 .js/.tsx 文件自动成为一个路由:
pages/
index.tsx → /
about.tsx → /about
blog/
index.tsx → /blog
[slug].tsx → /blog/:slug (动态路由)
- 动态路由:使用
[param]语法,pages/posts/[id].tsx匹配/posts/1、/posts/abc等。 - 嵌套路由:通过目录层级自然实现,
pages/dashboard/settings.tsx→/dashboard/settings。 - 浅层路由:允许改变 URL 而不重新触发数据获取,适合筛选参数等场景。
示例:动态路由页
// pages/posts/[id].tsx
import { useRouter } from 'next/router';
export default function Post() {
const router = useRouter();
const { id } = router.query;
return <div>文章 ID: {id}</div>;
}
App Router(新路由)
Next.js 13 引入了基于 app 目录的新路由系统,采用 React Server Components 架构,更加强大和灵活:
app/
layout.tsx → 根布局(所有页面共享)
page.tsx → /
about/
page.tsx → /about
blog/
layout.tsx → /blog 下的布局
page.tsx → /blog
[slug]/
page.tsx → /blog/:slug
- 约定文件:
page.tsx(页面内容)、layout.tsx(布局,保持状态)、loading.tsx(加载态)、error.tsx(错误边界)、not-found.tsx(404)。 - 动态路由:
[id]单段动态,[...slug]捕获所有后续路径段。 - 并行路由与拦截路由:支持高级 UI 模式,如模态框的独立路由。
App Router 示例:
// app/blog/[slug]/page.tsx
export default async function BlogPost({ params }) {
const { slug } = params;
// 可以直接在服务端获取数据,无需客户端 hooks
const post = await fetchPost(slug);
return <article>{post.content}</article>;
}
App Router 默认启用服务端组件,数据获取可以直接在组件内进行,减少了客户端 bundle 大小,性能更好。
数据获取(Data Fetching)
Next.js 提供了多种数据获取方式,适合不同的渲染策略。
Pages Router 数据获取
getStaticProps:在构建时运行,获取静态生成所需的数据。返回的 props 会注入到页面组件。getServerSideProps:在每个请求时运行,用于服务端渲染,可以获取实时数据。getStaticPaths:配合动态路由静态生成,返回所有可能的路径。
示例(静态生成):
export async function getStaticProps() {
const posts = await fetch('https://api.example.com/posts').then(res => res.json());
return { props: { posts }, revalidate: 60 }; // ISR:60秒后可在后台重新生成页面
}
export default function Blog({ posts }) {
// ...
}
App Router 数据获取
App Router 直接利用 async 组件和 fetch 的缓存策略。数据获取变得更简洁:
- 组件可以直接
await数据,Next.js 自动处理缓存和重新验证。 - 可以精细控制缓存:
fetch(url, { cache: 'force-cache' | 'no-store' })。 revalidate配置支持 ISR。
示例:
// app/posts/page.tsx
async function getPosts() {
const res = await fetch('https://api.example.com/posts', { next: { revalidate: 60 } });
return res.json();
}
export default async function PostsPage() {
const posts = await getPosts();
return <ul>{posts.map(post => <li key={post.id}>{post.title}</li>)}</ul>;
}
这种方式消除了对 getStaticProps 这类专用函数的依赖,心智负担更低。
静态生成(SSG: Static Site Generation)
静态生成指在构建时生成完整的 HTML 页面,然后这些页面可以被 CDN 缓存和快速分发。它是内容变化不频繁的场景下性能最优的选择,比如博客、文档站、产品介绍页。
Next.js 默认就会尝试对没有阻塞数据请求的页面进行静态生成(App Router 优先静态)。
如何实现静态生成
- Pages Router:使用
getStaticProps,如果没有该函数但页面没有服务端依赖,也会自动静态生成。 - App Router:组件默认就是服务端组件且无动态行为时,会自动静态渲染。可以通过
export const dynamic = 'force-static'强制静态。
ISR(增量静态再生成):允许在运行时重新生成静态页面,无需完整重建整个站点。在 getStaticProps 中设置 revalidate 或在 fetch 中配置 next.revalidate 即可。
示例:
// 构建时生成页面,最多每60秒重新验证一次
export async function getStaticProps() {
const data = await fetch('...');
return { props: { data }, revalidate: 60 };
}
这种策略兼顾了静态页面的性能和动态数据的实时性。
服务端渲染(SSR: Server-Side Rendering)
服务端渲染指每次请求时在服务器上生成 HTML 返回给客户端。适合需要最新数据且 SEO 重要的页面,比如新闻页、个性化首页、支付结果页。
Pages Router 中的 SSR
使用 getServerSideProps 函数:
export async function getServerSideProps(context) {
const { req, res } = context;
const data = await fetchDataBasedOnAuth(req.cookies.token);
return { props: { data } };
}
每个请求都会运行该函数,所以能拿到最新的请求信息(cookies、URL 参数等),但服务器负载较高,响应时间受 API 调用耗时影响。
App Router 中的 SSR
在 App Router 中,通过使用动态函数或设置动态渲染选项来触发 SSR:
// app/page.tsx
import { cookies } from 'next/headers';
export default async function Page() {
const cookieStore = cookies(); // 动态函数,强制该路由动态渲染
const data = await fetch('...');
// ...
}
也可以显式设置:export const dynamic = 'force-dynamic'。
优势:App Router 支持流式渲染,配合 Suspense 可向客户端提前发送部分 HTML,提升感知性能。
总结对比
| 策略 | 适用场景 | 数据获取方式 | 首屏性能 | SEO |
|------|----------|--------------|----------|-----|
| 静态生成(SSG) | 内容不频繁变化 | 构建时 | 极快(CDN) | 好 |
| 服务端渲染(SSR) | 实时数据、个性化 | 每次请求 | 较快 | 好 |
| 增量静态生成(ISR) | 内容定期更新 | 构建时 + 后台更新 | 快 | 好 |
| 客户端渲染(CSR) | 后台系统、交互密集 | 客户端请求 | 慢(白屏) | 差 |
Next.js 让这些策略的选择变得灵活,你甚至可以在同一应用内对不同页面使用不同的渲染方式,真正实现最佳平衡。