← 编程返回顶部

Next.js:React 全栈框架

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. 核心概念

模块重要概念优先级
RoutingApp Router、Layout、Dynamic Routes、Link必学
ComponentsServer Components、Client Components、use client必学
RenderingSSR、SSG、CSR、Hydration必学
DataServer Fetching、Caching、Revalidation必学
SEOMetadata、Open Graph、Sitemap必学
AssetsImage、Font、Static Assets重要
UXSuspense、Streaming、Loading、Error重要
BackendRoute Handlers、Server Actions按需
ArchitectureISR、Cache Components、PPR进阶
DeploymentStatic Export、Node Server、Docker重要

Next.js 与 React + Vite 的区别

能力React + Vite SPANext.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.tsxNot Found UI
route.tsHTTP Route Handler
default.tsxParallel 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]]

可以匹配零个或多个路径片段。

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

建议学习顺序:

  1. App Router 和文件路由。
  2. Server Components 与 Client Components。
  3. SSR、SSG、CSR 和 Hydration。
  4. Server-side Data Fetching。
  5. Metadata 和 SEO。
  6. Suspense、Streaming 和 Error Handling。
  7. Cache Components 与 Revalidation。
  8. Route Handlers、Server Actions。
  9. 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 需求,选择适合的服务器端与客户端渲染方式。


参考文档