Next.js 是由 Vercel 开发的基于 React 的全栈 Web 应用框架。它提供了生产环境所需的所有功能,包括服务端渲染(SSR)、静态站点生成(SSG)、API 路由等,让开发者能够轻松构建高性能的 Web 应用。
关于路由系统 :Next.js 有两种路由系统 - Pages Router (传统方式,基于
pages/目录)和 App Router (Next.js 13+ 推荐,基于app/目录)。本文主要介绍 App Router,这是当前推荐的方式,基于 React Server Components,提供更好的性能和开发体验。
版本说明:本文基于 Next.js 15+ 和 App Router 编写。部分特性在早期版本中可能不可用或行为不同。
Next.js 的核心功能
1. 多种渲染策略
Next.js 支持多种页面渲染策略,不同场景下性能表现不同。不同的组件可以使用不同的渲染策略,也可以混合使用。
- 静态站点生成 (SSG Static Site Generator): 构建时生成静态 HTML,直接提供给用户,性能最佳,默认的方式
- 增量静态再生成 (ISR Incremental Static Regeneration): 构建时生成静态页面,运行时可在后台定期重新生成,兼顾性能和实时性
- 服务端渲染 (SSR Server-Side Rendering): 每次请求时在服务器动态生成,数据最新
- 客户端渲染 (CSR Client-Side Rendering) : 适用于
Client Component,在浏览器中动态渲染
App Router 通过以下多种机制来决定使用哪种渲染策略,下面是常用的几种方式
- 路由段配置 (Route Segment Config) - 如
dynamic,revalidate,fetchCache等导出常量 - 动态 API (Dynamic APIs) - Next.js 提供的用于访问请求时数据的异步函数,如
cookies(),headers(),connection()等(Next.js 15+ 这些为异步函数)。使用这些 API 会让路由自动选择动态渲染(Dynamic Rendering) - Fetch 缓存选项 -
fetch()默认不缓存。可以通过cache: 'force-cache'开启缓存,或使用next.revalidate配置定期重新验证
2. 基于文件系统的路由
Next.js 使用基于文件系统的路由机制,无需额外配置路由。可用于 页面请求 和 API 请求
bash
app/
layout.js → 根布局
page.js → /
about/
page.js → /about
blog/
page.js → /blog
[id]/
page.js → /blog/:id (动态路由)
api/
users/
route.js → /api/users (API 路由)
3. 内置 CSS 支持
支持多种 CSS 方案:
- CSS Modules
- Sass/SCSS
- CSS-in-JS (styled-components, emotion 等)
- Tailwind CSS
4. 内置性能优化
Next.js 提供了多种内置的性能优化功能,开箱即用:
代码和构建优化
- 代码分割(Code Splitting):每个页面自动分割为独立的 JavaScript 包,只加载当前页面需要的代码
- Tree Shaking:构建时自动移除未使用的代码
- 压缩(Minification):自动压缩 JavaScript、CSS 和 HTML
- 预加载(Prefetching):自动预加载可见链接的页面代码,提升导航速度
- 快速刷新(Fast Refresh):开发时修改代码后自动更新,保持组件状态,无需刷新页面
图片和字体优化
- 图片优化 :
Image组件自动优化图片格式(WebP、AVIF)、尺寸和懒加载 - 字体优化:自动内联字体 CSS,消除字体加载闪烁(FOIT/FOUT)
- 响应式图片:根据设备自动选择合适尺寸的图片
渲染优化
- Server Components:默认在服务器渲染,减少客户端 JavaScript 包大小
- 流式渲染(Streaming):页面内容可以逐步发送,用户更快看到内容
- 并行数据获取:Server Components 中可以并行获取多个数据源
- 增量静态再生成(ISR):静态页面可以在后台定期更新,兼顾性能和实时性
缓存策略
- 自动静态优化:没有动态数据的页面自动生成为静态 HTML
- 全路由缓存:构建时缓存渲染结果
- 数据缓存 :
fetch请求默认缓存,避免重复请求 - 客户端路由缓存:导航时缓存页面数据,实现即时导航
性能监控
- Web Vitals 支持:内置 Core Web Vitals 监控
- 性能分析工具 :提供
next/bundle-analyzer分析包大小
示例:性能对比(具体数值因应用而异)
diff
传统 React SPA:
- 首次加载:下载全部 JS(可能较大,如 500KB+)
- 首屏渲染:空白页 → JS 加载 → React 渲染 → 显示内容(通常较慢)
- SEO:搜索引擎只能看到空白 HTML
Next.js (App Router):
- 首次加载:预渲染的 HTML + 必需的 JS(通常更小)
- 首屏渲染:立即显示内容(First Contentful Paint 更快)
- SEO:搜索引擎直接看到完整内容
关键优势:
- 更快的首屏渲染:用户立即看到内容
- 更小的 JavaScript 包:Server Components 代码不发送到客户端
- 更好的 SEO:搜索引擎能抓取完整的 HTML
- 零配置:大部分优化自动生效,无需手动配置
如何使用 Next.js
创建项目
使用 create-next-app 快速创建项目:
bash
npx create-next-app@latest my-app
cd my-app
npm run dev
访问 http://localhost:3000 即可看到应用。
项目结构
在 App Router 中,文件名有特殊含义,下面是典型项目结构:
bash
app/
layout.js → 根布局(必需)
page.js → 首页 (/)
loading.js → 加载状态
error.js → 错误处理
not-found.js → 404 页面
about/
page.js → /about
blog/
page.js → /blog
[id]/
page.js → /blog/:id (动态路由)
api/
users/
route.js → /api/users (API 端点)
| 文件名 | 用途 | 说明 |
|---|---|---|
page.js |
页面组件 | 定义可访问的路由,必需 |
layout.js |
布局组件 | 包裹所有子路由,可嵌套 |
loading.js |
加载状态 | 显示 Suspense 边界,数据加载时展示 |
error.js |
错误处理 | Error Boundary,捕获子路由错误 |
not-found.js |
404 页面 | 处理找不到的路由 |
template.js |
模板组件 | 类似 layout,但每次导航都重新创建实例 |
route.js |
API 路由 | 定义 HTTP 请求处理器 (GET/POST/PUT 等) |
default.js |
平行路由默认页 | 处理平行路由加载失败时的回退 |
基本页面示例
1. Server Component(默认)
默认情况下,所有组件都是 Server Component,在服务器上渲染:
javascript
// app/posts/page.js
async function getPosts() {
// fetch 默认不缓存,可以添加 { cache: 'force-cache' } 开启缓存
const res = await fetch("https://api.example.com/posts");
return res.json();
}
// 默认是 Server Component,在服务器执行
export default async function PostsPage() {
const posts = await getPosts();
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
2. Client Component
需要交互(useState、useEffect 等)时,使用 'use client' 标记:
javascript
// app/components/Counter.js
"use client"; // 标记为 Client Component
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>Count: {count}</button>;
}
3. 动态路由
使用 [参数名] 创建动态路由:
javascript
// app/blog/[id]/page.js
export default async function BlogPost({ params }) {
const { id } = params;
const post = await fetch(`https://api.example.com/posts/${id}`).then((r) => r.json());
return (
<article>
<h1>{post.title}</h1>
<p>{post.content}</p>
</article>
);
}
Next.js 基本原理
1. 渲染流程
Next.js 的核心是其灵活的渲染策略:
yaml
用户请求 → Next.js Server
↓
判断页面类型
↓
├─ SSG: 返回预生成的 HTML (构建时生成)
├─ ISR: 检查缓存 → 返回缓存 / 后台重新生成
├─ SSR: 服务器渲染 → 生成 HTML → 返回
└─ CSR: 返回基础 HTML → 客户端渲染
↓
返回给客户端
↓
React Hydration (水合)
↓
交互式应用
2. 页面从加载到交互的完整过程
当用户访问一个 Next.js (App Router) 页面时,会经历以下阶段:
阶段 1: 服务器渲染 Server Components
javascript
// app/blog/page.js
// ========== Server Component(只在服务器运行)==========
async function getData() {
// 可以直接访问数据库、读取文件等
const data = await db.query("SELECT * FROM posts");
return data;
}
export default async function BlogPage() {
const data = await getData();
// 这个组件在服务器渲染完成后,
// 只有渲染结果(HTML)会发送到客户端
// 这段代码本身不会发送到浏览器
return (
<div>
<h1>{data.title}</h1>
<p>{data.content}</p>
{/* 需要交互的部分使用 Client Component */}
<LikeButton />
</div>
);
}
阶段 2: 生成并发送响应
服务器生成并发送:
- Server Component 渲染后的 HTML(完整内容)
- RSC Payload(React Server Component Payload)- 服务端组件的序列化表示
- Client Component 的 JavaScript 代码
- 必要的 React 运行时
阶段 3: 浏览器显示 + Hydration
javascript
// app/components/LikeButton.js
// ========== Client Component(在浏览器运行)==========
"use client";
import { useState } from "react";
export default function LikeButton() {
const [count, setCount] = useState(0);
// 这段代码会发送到浏览器并执行
return <button onClick={() => setCount(count + 1)}>点赞 {count}</button>;
}
完整时间线
css
用户访问页面
↓
[服务器] 执行 Server Component (getData)
↓
[服务器] 渲染 Server Component → 生成 HTML
↓
[服务器] 生成 RSC Payload(Server Component 的序列化表示)
↓
[网络] 发送 HTML + RSC Payload + Client Component JS
↓
[浏览器] 显示 HTML(用户立即看到内容)
↓
[浏览器] 下载并执行 Client Component JS
↓
[浏览器] Hydration(激活 Client Components)
↓
[浏览器] 页面完全可交互(按钮可以点击)
关键优势
- Server Component 代码不会发送到客户端:减少 JavaScript 包大小
- 更快的首屏渲染:用户立即看到完整的 HTML
- 更好的 SEO:搜索引擎能抓取完整内容
- 服务器能力:可以直接访问数据库、文件系统等
3. 代码分割机制
Next.js 自动进行代码分割:
javascript
// app/page.js
import dynamic from "next/dynamic";
// 动态导入,只在需要时加载
const HeavyComponent = dynamic(() => import("./components/Heavy"), {
loading: () => <p>Loading...</p>,
ssr: false, // 禁用服务端渲染
});
export default function Home() {
return <HeavyComponent />;
}
原理:
- 自动分割:每个页面自动成为一个代码块
- 动态导入 :使用
dynamic()或import()实现按需加载 - 预加载:Next.js 自动预加载可见链接的页面代码
4. Server Components 原理
React Server Components 是 Next.js 13+ 的核心:
arduino
服务器 客户端
↓
渲染 Server Component
↓
生成 RSC Payload(序列化表示)
↓
发送到客户端 ──────────────────→ 接收 RSC Payload
↓
重建 React 树
↓
Hydrate Client Components
↓
完整的交互式应用
关键优势:
- Server Component 的代码不会发送到客户端
- 可以直接访问数据库或文件系统
- 减少 JavaScript bundle 大小
- 提升初始加载性能
5. 静态优化
Next.js 会自动分析页面并进行静态优化。对于不需要动态数据的页面,Next.js 会自动在构建时生成静态 HTML:
javascript
// app/about/page.js
// 没有 fetch 或动态函数,自动静态优化
export default function About() {
return <div>About Page</div>;
}
构建输出示例:
scss
Route (app) Size First Load JS
┌ ○ / 1.2 kB 85 kB
├ ○ /about 850 B 83 kB
├ ○ /blog 2.1 kB 87 kB
├ ƒ /blog/[id] 1.5 kB 86 kB
└ λ /dashboard 1.8 kB 86 kB
○ (Static) 静态生成 (SSG)
ƒ (Dynamic) 动态路由 + SSG/ISR
λ (Server) 服务端渲染 (SSR)
性能优化建议
1. 选择合适的渲染策略
- SSG: 博客、文档、营销页面(内容稳定)
- ISR: 电商列表、新闻(内容更新频繁但可容忍延迟)
- SSR: 个人仪表盘、实时数据(需要最新数据)
- CSR: 交互密集型应用(如编辑器)
2. 使用 Image 组件
javascript
import Image from "next/image";
export default function Avatar() {
return (
<Image
src="/avatar.jpg"
alt="Avatar"
width={200}
height={200}
placeholder="blur" // 模糊占位符
priority // 优先加载
/>
);
}
3. 使用 Link 预加载
javascript
import Link from "next/link";
export default function Nav() {
return (
<Link href="/about" prefetch={true}>
About
</Link>
);
}
生态及参考资源
Next.js 拥有丰富的生态系统:
- Vercel: 官方托管平台,零配置部署
- next-auth: 身份认证解决方案
- SWR: 数据获取 Hook
- next-i18next: 国际化
- next-seo: SEO 优化
- next-pwa: PWA 支持
参考