Next.js 是基于 React 的 Web 框架,提供路由、服务端渲染、静态生成、数据获取、缓存、SEO 和后端能力。
与普通 React SPA 相比,Next.js 最重要的新增能力是:
- 基于文件系统的 App Router。
- React Server Components。
- SSR、SSG、ISR 等渲染策略。
- 服务端数据获取与缓存。
- Metadata、Sitemap 等 SEO 功能。
- Route Handlers 和 Server Actions。
- 图片、字体及生产构建优化。
1. 核心概念
| 模块 | 重要概念 | 优先级 |
|---|---|---|
| Routing | App Router、Layout、Dynamic Routes、Link | 必学 |
| Components | Server Components、Client Components、use client | 必学 |
| Rendering | SSR、SSG、CSR、Hydration | 必学 |
| Data | Server Fetching、Caching、Revalidation | 必学 |
| SEO | Metadata、Open Graph、Sitemap | 必学 |
| Assets | Image、Font、Static Assets | 重要 |
| UX | Suspense、Streaming、Loading、Error | 重要 |
| Backend | Route Handlers、Server Actions | 按需 |
| Architecture | ISR、Cache Components、PPR | 进阶 |
| Deployment | Static Export、Node Server、Docker | 重要 |
Next.js 与 React + Vite 的区别
| 能力 | React + Vite SPA | Next.js |
|---|---|---|
| React UI | 支持 | 支持 |
| 客户端路由 | 通常使用 React Router | 内置 App Router |
| CSR | 支持 | 支持 |
| SSR | 需要额外方案 | 内置 |
| SSG | 需要额外方案 | 内置 |
| React Server Components | 不属于普通 Vite SPA 的默认能力 | 内置 |
| API Routes | 需要独立后端 | Route Handlers |
| SEO Metadata | 自行管理 | 内置 Metadata API |
| 图片优化 | 第三方工具或自行实现 | next/image |
| 数据缓存 | 通常由 TanStack Query 等管理 | 服务端缓存 + 可选客户端缓存 |
| 生产部署 | 通常是静态文件 | 静态文件或 Node.js Server |
Next.js 并不取代 React,而是在 React 基础上增加路由、渲染、服务端功能和构建约定。
2. 安装与项目结构
创建项目:
pnpm create next-app@latest my-site
常用命令:
# 开发服务器
pnpm dev
# 生产构建
pnpm build
# 启动生产服务器
pnpm start
典型项目结构:
my-site/
├── app/
│ ├── layout.tsx
│ ├── page.tsx
│ ├── globals.css
│ ├── loading.tsx
│ ├── error.tsx
│ ├── not-found.tsx
│ │
│ ├── about/
│ │ └── page.tsx
│ │
│ ├── blog/
│ │ ├── page.tsx
│ │ └── [slug]/
│ │ └── page.tsx
│ │
│ └── api/
│ └── health/
│ └── route.ts
│
├── components/
├── lib/
├── public/
├── next.config.ts
└── package.json
其中:
app/:基于文件系统的路由。components/:可复用组件。lib/:工具函数、数据访问等。public/:静态资源。next.config.ts:Next.js 配置。
3. App Router:文件系统路由
Next.js 使用文件和文件夹定义路由。
例如:
app/
├── page.tsx
├── about/
│ └── page.tsx
└── blog/
└── [slug]/
└── page.tsx
对应:
/ → app/page.tsx
/about → app/about/page.tsx
/blog/react → app/blog/[slug]/page.tsx
/blog/typescript → app/blog/[slug]/page.tsx
特殊文件
| 文件 | 作用 |
|---|---|
page.tsx | 定义路由页面 |
layout.tsx | 定义共享布局 |
template.tsx | 类似 Layout,但导航时可重新创建实例 |
loading.tsx | 路由加载状态 |
error.tsx | 路由错误边界 |
not-found.tsx | Not Found UI |
route.ts | HTTP Route Handler |
default.tsx | Parallel Routes 的默认 UI |
Layout
// app/layout.tsx
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<header>My Website</header>
<main>{children}</main>
<footer>Copyright</footer>
</body>
</html>
);
}
Layout 可以嵌套。
例如:
app/
├── layout.tsx
└── blog/
├── layout.tsx
└── page.tsx
blog/layout.tsx 只作用于 Blog 路由及其子路由。
共享 Layout 通常可以在客户端导航时保持挂载状态,避免不必要的重新渲染和状态丢失。
4. Dynamic Routes
动态路由通过方括号定义:
app/blog/[slug]/page.tsx
访问:
/blog/react-basics
对应:
type Props = {
params: Promise<{
slug: string;
}>;
};
export default async function BlogPage({ params }: Props) {
const { slug } = await params;
return <h1>{slug}</h1>;
}
Next.js 15/16 中,App Router 的 params 是 Promise,需要异步读取。
Catch-all Routes
[...slug]
例如:
/docs/react/hooks
可能获得:
{
slug: ["react", "hooks"];
}
Optional Catch-all Routes
[[...slug]]
可以匹配零个或多个路径片段。
5. Link 与客户端导航
Next.js 提供 next/link:
import Link from "next/link";
export default function Navbar() {
return (
<nav>
<Link href="/">Home</Link>
<Link href="/blog">Blog</Link>
<Link href="/about">About</Link>
</nav>
);
}
主要功能:
- 客户端导航。
- 路由预取。
- 减少不必要的整页刷新。
- 与 App Router 的客户端路由缓存协作。
useRouter
如果需要在事件处理函数中导航:
"use client";
import { useRouter } from "next/navigation";
export function NavigationButton() {
const router = useRouter();
return <button onClick={() => router.push("/blog")}>Open Blog</button>;
}
常用方法:
| 方法 | 作用 |
|---|---|
router.push() | 导航并添加历史记录 |
router.replace() | 导航并替换历史记录 |
router.back() | 返回上一页 |
router.refresh() | 请求当前路由新的服务端内容 |
router.prefetch() | 预取路由 |
router.refresh() 并不等于清空所有服务端缓存。若数据来自仍然有效的服务端缓存,刷新后可能仍然看到缓存数据。
6. Server Components
在 App Router 中,Page 和 Layout 默认是 Server Component。
例如:
// app/blog/page.tsx
export default async function BlogPage() {
const posts = await getPosts();
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
Server Component 的特点:
- 在服务器环境执行。
- 可以直接访问服务端数据库、文件或私有 API。
- 可以使用 async/await。
- 不会将自身的组件实现代码作为 Client Component JavaScript 发送到浏览器。
- 不能直接使用
useState、useEffect等客户端 Hook。 - 不能直接访问
window、document等浏览器 API。
Server Component 的价值
Browser Request
↓
Next.js Server
↓
Server Component
↓
Fetch Data
↓
Render RSC Payload / HTML
↓
Browser
可以减少浏览器需要执行的 JavaScript,并且让数据获取发生在靠近服务器资源的位置。
注意:Server Component 默认在服务器执行,不意味着它每次请求都必须重新执行;实际行为还取决于预渲染和缓存策略。
7. Client Components
需要浏览器交互时使用:
"use client";
import { useState } from "react";
export function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>Count: {count}</button>;
}
Client Component 适用于:
useState、useEffect。- 用户交互事件。
- React Hook Form。
- TanStack Query Hooks。
- 浏览器 API。
- 需要浏览器环境的第三方组件。
use client 的实际含义
"use client" 声明客户端模块边界。
被标记模块及其静态导入依赖会进入客户端模块图。
但需要注意:
Client Component 并不意味着首屏完全不在服务器生成 HTML。
Next.js 可以预渲染 Client Component 的初始 HTML,随后浏览器加载 JavaScript 并进行 Hydration。
推荐组件组织方式
BlogPage (Server Component)
│
├── ArticleContent (Server Component)
│
├── TableOfContents (Client Component)
│
└── LikeButton (Client Component)
原则:
默认保留 Server Component,只把需要交互的部分划分为 Client Component。
不要为了方便,在整个页面或根 Layout 上随意添加 "use client"。
8. Server Component 与 Client Component 的组合
Server Component 可以导入并渲染 Client Component。
// app/blog/page.tsx
import LikeButton from "@/components/LikeButton";
export default async function BlogPage() {
const post = await getPost();
return (
<article>
<h1>{post.title}</h1>
<p>{post.content}</p>
<LikeButton postId={post.id} />
</article>
);
}
Client Component:
"use client";
import { useState } from "react";
export default function LikeButton({ postId }: { postId: number }) {
const [liked, setLiked] = useState(false);
return (
<button onClick={() => setLiked(!liked)}>{liked ? "Liked" : "Like"}</button>
);
}
Server Component 向 Client Component 传递 Props 时,数据通常需要满足 React 的序列化要求。
普通函数不能随意跨越 Server/Client 边界传递;Server Functions 有特殊的传递机制。
9. SSR、SSG、CSR、ISR
这四个概念主要描述渲染与内容更新策略。
| 模式 | 含义 | 适用场景 |
|---|---|---|
| CSR | 主要在浏览器中渲染页面内容 | 高交互应用 |
| SSR | 请求时在服务器生成 HTML | 实时或个性化内容 |
| SSG | 构建时预先生成 HTML | 博客、文档 |
| ISR | 部署后按策略更新已生成内容 | 定期更新的页面 |
CSR
Browser
↓
Load HTML + JavaScript
↓
React Executes
↓
Fetch Data
↓
Render UI
典型应用:普通 React SPA。
SSR
Browser
↓
Request Page
↓
Server Fetches Data
↓
Server Renders HTML
↓
Browser Receives HTML
↓
Hydration
适合内容需要在请求时确定的页面。
SSG
Build Time
↓
Generate HTML
↓
Deploy Static Files
↓
Browser
适合博客、文档、About 页面等较少变化的内容。
ISR
Static Content
↓
Cache Lifetime / Revalidation
↓
Regenerate or Refresh
↓
Updated Content
ISR 不是简单地定时在后台重新构建整个网站,而是利用 Next.js 缓存与重新验证机制更新对应内容。
在 Next.js 16 中,应根据是否使用 Cache Components 选择相应的配置模型。
10. Hydration
Hydration 指 React 在浏览器中接管服务器提供的 HTML,并建立交互能力。
Server Generated HTML
↓
Browser Displays HTML
↓
Download Client JS
↓
React Hydration
↓
Interactive UI
例如服务器已经输出:
<button>Count: 0</button>
浏览器完成 Hydration 后,这个按钮才获得 React 定义的交互行为。
Hydration Mismatch
如果服务器生成的 HTML 与浏览器首次渲染结果不一致,可能发生 Hydration 错误。
常见原因:
- 渲染阶段直接读取
window或localStorage。 - 服务端和客户端输出不同的时间。
- 使用未固定的随机值。
- 不一致的条件渲染。
- 无效的 HTML 嵌套。
原则是保证首轮服务端和客户端渲染结果一致。
11. Server-side Data Fetching
Server Component 可以直接执行异步操作。
export default async function BlogPage() {
const response = await fetch("https://api.example.com/posts");
if (!response.ok) {
throw new Error("Failed to fetch posts");
}
const posts = await response.json();
return <PostList posts={posts} />;
}
与客户端获取数据的区别
Server Fetching:
Next.js Server
↓
External API / Database
↓
Server Component
↓
Rendered Result
Client Fetching:
Browser
↓
TanStack Query
↓
Axios / Fetch
↓
Backend API
Server Fetching 适合:
- 文章内容。
- SEO 相关数据。
- 首屏需要展示的公开数据。
- 不希望把私有 API 凭据暴露到浏览器的数据访问。
Client Fetching 适合:
- 高交互数据。
- 需要持续更新的数据。
- 用户操作驱动的查询。
- 本地缓存和乐观更新。
两者可以混合使用。
12. Next.js 缓存机制
缓存是 App Router 中比较复杂的部分,因为不同版本有不同的配置模型。
需要区分:
| 缓存类型 | 主要作用 |
|---|---|
| Request Memoization | 同一次 React 服务端渲染内去重 |
| Server Data / Cache Components Cache | 在请求之间复用服务端数据或组件结果 |
| Router Cache | 浏览器端缓存路由相关 RSC Payload |
| Browser HTTP Cache | 缓存 JS、CSS、图片等静态资源 |
Request Memoization
同一次 Server Component 渲染中,相同的 GET Fetch 请求可以复用结果。
const post1 = await fetch(url);
const post2 = await fetch(url);
在满足相同请求条件时,可以避免重复的实际请求。
但它不等于跨 HTTP 请求的持久缓存。
默认 fetch 行为
在现代 Next.js App Router 中:
const response = await fetch(url);
默认不使用持久的数据缓存。
如果页面需要跨请求复用数据,需要采用适当的缓存策略。
以下主要介绍 Next.js 16 的 Cache Components 模型。
13. Cache Components
启用:
// next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
cacheComponents: true,
};
export default nextConfig;
cacheComponents 可以使用以下能力:
"use cache"。cacheLife()。cacheTag()。revalidateTag()。updateTag()。- 与 Suspense 协作的部分预渲染。
Cache Components 不意味着全部内容自动缓存,而是开发者明确声明适合复用的内容。
use cache
import { cacheLife } from "next/cache";
export default async function BlogPage() {
"use cache";
cacheLife("hours");
const posts = await getPosts();
return <PostList posts={posts} />;
}
这里缓存的是函数或组件的结果,而不仅仅是某一个 Fetch Response。
cacheLife
cacheLife("hours");
用于声明缓存生命周期。
也可以配置自定义缓存策略。
cacheTag
import { cacheTag } from "next/cache";
export async function getPosts() {
"use cache";
cacheTag("posts");
return fetchPostsFromDatabase();
}
Tag 使得多个相关缓存可以通过同一个标识进行重新验证。
14. Revalidation:缓存重新验证
Next.js 16 Cache Components 提供几种重要方法。
revalidateTag
import { revalidateTag } from "next/cache";
revalidateTag("posts", "max");
将相关缓存标记为过期,使用 Stale-While-Revalidate 模式进行更新。
也就是说,允许先返回旧内容,再执行后台刷新。
适用于博客、产品目录等可以容忍短时间旧数据的内容。
updateTag
import { updateTag } from "next/cache";
updateTag("posts");
立即使相关缓存失效,下一次读取需要获得新数据,而不是继续返回旧内容。
updateTag() 只能用于 Server Actions。
revalidatePath
import { revalidatePath } from "next/cache";
revalidatePath("/blog");
按照路由路径重新验证相关缓存。
对比
| 方法 | 作用 | 常见用途 |
|---|---|---|
cacheLife | 定义缓存生命周期 | 定时更新 |
cacheTag | 为缓存打标签 | 缓存分组 |
revalidateTag | 失效并后台刷新 | 博客、内容列表 |
updateTag | 立即失效 | 用户提交后的即时一致性 |
revalidatePath | 按路径重新验证 | 页面内容更新 |
这里的缓存失效管理发生在 Next.js 服务端,与 TanStack Query 的 queryClient.invalidateQueries() 不属于同一个缓存系统。
15. Suspense 与 Streaming
Next.js 支持 React Suspense 和流式渲染。
Suspense
import { Suspense } from "react";
export default function Page() {
return (
<main>
<h1>Dashboard</h1>
<Suspense fallback={<p>Loading...</p>}>
<Analytics />
</Suspense>
</main>
);
}
允许将等待异步结果的部分单独放入加载边界。
Streaming
Browser Request
↓
Server
│
├── Send Static HTML
│
├── Send Loading UI
│
└── Send Async Content Later
这样不必等待所有数据都获取完成后才发送整个页面。
loading.tsx
// app/blog/loading.tsx
export default function Loading() {
return <p>Loading blog...</p>;
}
Next.js 会为对应路由建立加载边界。
16. Error Handling
error.tsx
"use client";
export default function ErrorPage({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<div>
<p>Something went wrong.</p>
<button onClick={reset}>Try again</button>
</div>
);
}
error.tsx 是路由段错误边界,需要使用 Client Component。
它主要用于处理对应组件树中可由 Error Boundary 捕获的错误,并不意味着能捕获所有类型的服务器或事件处理错误。
not-found.tsx
export default function NotFound() {
return <h1>Page not found</h1>;
}
配合:
import { notFound } from "next/navigation";
if (!post) {
notFound();
}
可以显示 Not Found 页面。
17. Metadata 与 SEO
Next.js 提供 Metadata API,方便为页面生成 SEO 元信息。
静态 Metadata
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "My Blog",
description: "Articles about programming and technology",
};
动态 Metadata
import type { Metadata } from "next";
type Props = {
params: Promise<{ slug: string }>;
};
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { slug } = await params;
const post = await getPost(slug);
return {
title: post.title,
description: post.summary,
};
}
适用于动态博客文章。
Open Graph
export const metadata: Metadata = {
title: "My Blog",
openGraph: {
title: "My Blog",
description: "Programming articles",
type: "website",
images: ["/og-image.png"],
},
};
Sitemap
// app/sitemap.ts
import type { MetadataRoute } from "next";
export default function sitemap(): MetadataRoute.Sitemap {
return [
{
url: "https://example.com",
lastModified: new Date(),
},
{
url: "https://example.com/blog",
lastModified: new Date(),
},
];
}
还可以使用文件约定生成 robots.txt、Open Graph 图片等。
注意:SSR/SSG 有利于搜索引擎直接获取内容,但不意味着使用 Next.js 就能自动获得良好的 SEO 排名。
18. generateStaticParams
动态路由可以使用 generateStaticParams() 在构建阶段预生成已知页面。
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
const posts = await getPosts();
return posts.map((post) => ({
slug: post.slug,
}));
}
对于 Markdown 或 MDX 博客,可以在构建时获取文章 Slug,并生成对应页面。
Markdown Files
↓
generateStaticParams()
↓
Known Slugs
↓
Static Blog Pages
这对个人博客非常实用。
如果需要纯静态导出,还必须确保所有动态路由满足静态导出的限制。
19. next/image
Next.js 提供内置 Image 组件。
import Image from "next/image";
export default function Profile() {
return <Image src="/avatar.jpg" alt="Avatar" width={300} height={300} />;
}
主要功能:
- 响应式图片。
- 图片尺寸优化。
- 减少布局偏移。
- 原生 Lazy Loading。
- 根据环境提供优化后的图片资源。
对于远程图片,需要在 next.config.ts 中配置允许的远程来源。
注意:默认动态图片优化通常需要支持图片优化的运行环境。纯静态导出时应使用自定义 Loader 或关闭内置优化等兼容方式。
20. next/font
Next.js 支持字体加载与优化。
例如:
import { Geist } from "next/font/google";
const geist = Geist({
subsets: ["latin"],
});
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={geist.className}>{children}</body>
</html>
);
}
主要价值:
- 优化字体加载。
- 减少不必要的字体网络请求。
- 减轻字体切换造成的布局偏移。
- 支持构建时自托管字体资源。
21. Route Handlers
Next.js 可以使用 route.ts 定义 HTTP API。
例如:
// app/api/health/route.ts
export async function GET() {
return Response.json({
status: "ok",
});
}
对应:
GET /api/health
也可以使用 POST:
export async function POST(request: Request) {
const body = await request.json();
return Response.json({
received: body,
});
}
Route Handlers 支持常见 HTTP 方法:
GET
POST
PUT
PATCH
DELETE
HEAD
OPTIONS
与 Django DRF 的关系
如果项目已经采用 Django REST Framework 作为后端:
Next.js Frontend
↓
Django DRF API
↓
PostgreSQL
不一定需要再通过 Next.js Route Handlers 重写一套 API。
Route Handlers 可以用于:
- 轻量后端接口。
- Webhook。
- 某些服务端代理或聚合。
- Next.js 专属服务端功能。
22. Server Actions / Server Functions
Server Actions 允许客户端交互触发服务端函数。
例如:
// app/actions.ts
"use server";
export async function createPost(formData: FormData) {
const title = formData.get("title");
// Validate input and check authorization
// Save post to database
}
表单:
import { createPost } from "./actions";
export default function PostForm() {
return (
<form action={createPost}>
<input name="title" />
<button type="submit">Create</button>
</form>
);
}
执行过程:
Browser Form
↓
Server Action
↓
Validation / Authorization
↓
Database / Backend API
↓
Revalidation
↓
Updated UI
Server Actions 适合处理表单提交、数据修改等操作。
需要注意:
- Server Actions 不是普通的前端函数。
- 客户端调用会触发服务端通信。
- 必须执行服务端验证和权限检查。
- 不应认为只要函数写在服务器文件中就天然安全。
- 不适合替代所有传统 HTTP API。
如果已经有 Django + Orval + TanStack Query 的完整 API 架构,不必强行迁移到 Server Actions。
23. proxy.ts
在 Next.js 16 中,proxy.ts 取代了旧的 Middleware 文件命名约定。
例如:
// proxy.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function proxy(request: NextRequest) {
return NextResponse.next();
}
可以用于:
- 请求重定向。
- URL 重写。
- 某些访问控制前置逻辑。
- 根据请求信息进行路由处理。
但不建议在 Proxy 中执行大量复杂数据库操作。
此外,Proxy 中的前置权限检查不能代替后端真正的认证和授权。
例如涉及用户私有数据的接口,仍必须在实际服务端处理逻辑中验证权限。
24. Turbopack
Turbopack 是 Next.js 的构建工具。
主要目标包括:
- 加快开发服务器启动。
- 提高增量编译效率。
- 缩短大型项目的更新反馈时间。
- 支持 Next.js 的开发和生产构建流程。
它与 Vite 都属于构建工具相关技术,但服务的框架生态和实现方式不同。
React + Vite
↓
Vite Build Pipeline
Next.js
↓
Turbopack
Next.js 16 已将 Turbopack 作为开发及生产构建的默认构建器。
部分需要特殊构建配置的项目仍然需要检查插件及构建兼容性。
25. Static Export
Next.js 可以构建为静态网站。
配置:
// next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
output: "export",
};
export default nextConfig;
执行:
pnpm build
通常生成:
out/
├── index.html
├── about.html
├── blog/
└── _next/
└── static/
随后可以交给 Nginx、对象存储或 CDN。
Static Export 的限制
纯静态导出不支持依赖请求时服务器执行的功能,例如:
- 动态 SSR。
- Server Actions。
- 需要服务器处理的 Route Handlers。
- 请求时读取 Cookie/Header 的动态页面。
- 依赖 Next.js 服务器的动态图片优化。
- Proxy。
但是 Server Components 并不意味着一定需要 Node.js 服务器长期运行。
如果 Server Components 所需数据可以在构建时取得,它们仍然可以参与静态预渲染。
26. Node.js Server 与 Docker
如果需要 SSR、Server Actions、动态数据等功能,可以部署 Node.js 服务器。
启动:
pnpm build
pnpm start
也可以配置 Standalone 输出:
// next.config.ts
const nextConfig = {
output: "standalone",
};
export default nextConfig;
Standalone 可以生成更适合独立服务器部署的输出。
典型架构:
Browser
↓
Nginx
↓
Next.js Node Server
│
├── Server Components
├── Route Handlers
├── Server Actions
└── Static Assets
如果同时使用 Django:
Browser
↓
Nginx
│
├── / → Next.js
│
└── /api/ → Django DRF
Docker 部署
可以使用多阶段 Dockerfile:
Stage 1: Install Dependencies
↓
Stage 2: Next.js Build
↓
Stage 3: Production Runtime
如果采用静态导出,最终镜像可以只包含 Nginx。
如果采用动态服务器渲染,则需要 Node.js 运行环境或其他受支持的部署平台。
27. Next.js 与 TanStack Query
Next.js 的服务端数据获取和 TanStack Query 并不互相排斥。
Server Component
适合直接获取服务端数据:
export default async function Page() {
const posts = await getPosts();
return <PostList posts={posts} />;
}
Client Component
适合需要交互和客户端缓存的内容:
"use client";
function Profile() {
const { data } = useUserProfileRetrieve();
return <div>{data?.username}</div>;
}
选型原则
| 场景 | 优先考虑 |
|---|---|
| 博客文章正文 | Server Component |
| SEO 内容 | Server Fetching |
| 公开、较稳定的数据 | Server Cache |
| 个人资料编辑 | Client Component + RHF |
| 复杂筛选搜索 | TanStack Query |
| 乐观更新 | TanStack Query |
| 表单提交 | Server Actions 或 Mutation |
| 实时交互数据 | Client Fetching / 订阅 |
不需要因为采用 Next.js,就删除已有的 TanStack Query。
但也不必为了使用 TanStack Query,把所有 Server Components 都改成 Client Components。
28. Next.js 常见问题
Q1:Next.js 是否完全替代 React Router?
对于使用 App Router 的 Next.js 项目,通常不再需要 React Router。
因为 Next.js 已经提供文件系统路由和客户端导航。
Q2:Next.js 页面是否默认全部 SSR?
不是。
页面可能预渲染、动态渲染、部分预渲染,也可以包含客户端渲染部分。
不能简单认为 Next.js = SSR。
Q3:Server Component 一定比 Client Component 快吗?
不一定。
Server Component 通常可以减少客户端 JavaScript,但也可能增加服务端计算与数据获取的等待时间。
实际性能取决于内容类型、缓存策略、网络条件和组件结构。
Q4:Server Component 能不能使用 React Hook Form?
不能直接使用依赖客户端 Hook 的 React Hook Form。
应该将表单放在 Client Component 中。
Q5:Next.js 适合纯静态个人博客吗?
适合。
可以结合:
- MDX。
- App Router。
- generateStaticParams。
- Metadata。
- Static Export。
但如果网站主要是纯静态内容,也可以考虑 Astro、Hugo 等更加专注于内容站点的工具。
Q6:Next.js 是否必须有独立后端?
不是。
Next.js 提供 Route Handlers 和 Server Actions。
但如果项目已经拥有 Django DRF 这样的完整后端,Next.js 可以专注于前端渲染与 SEO,不必重复建设相同的业务后端。
总结
Next.js 的核心知识可以分为四个层次:
| 层次 | 核心概念 |
|---|---|
| 基础路由 | App Router、Layout、Dynamic Routes、Link |
| 渲染模型 | Server Component、Client Component、SSR、SSG、Hydration |
| 数据与缓存 | Server Fetching、use cache、cacheLife、Revalidation |
| 工程能力 | Metadata、Streaming、Route Handlers、Server Actions、Deployment |
建议学习顺序:
- App Router 和文件路由。
- Server Components 与 Client Components。
- SSR、SSG、CSR 和 Hydration。
- Server-side Data Fetching。
- Metadata 和 SEO。
- Suspense、Streaming 和 Error Handling。
- Cache Components 与 Revalidation。
- Route Handlers、Server Actions。
- Static Export、Docker 和生产部署。
核心架构可以概括为:
React Components
↓
Next.js App Router
│
├── Server Components
│ ├── Data Fetching
│ ├── Server Cache
│ └── HTML / RSC Payload
│
└── Client Components
├── React Hooks
├── User Interaction
├── React Hook Form
└── TanStack Query
↓
Backend API
Next.js 的主要价值并不是让所有 React 代码在服务器运行,而是允许开发者根据数据、交互、缓存和 SEO 需求,选择适合的服务器端与客户端渲染方式。