Skip to main content

Next.js 面试高频与场景题

Next.js 面试题分三类:基础概念(构建工具 / 渲染模式)架构设计(App Router / RSC 边界)场景题(综合应用)。本篇按难度梯度整理高频题 + 答题框架。

一、基础概念题

❓ 题 1:Next.js 15 的渲染模型是什么?和传统 React SPA 有什么不同?

答题框架

传统 SPA:
- 所有组件都是 Client Component
- 浏览器拿到空 HTML,下载 JS,React 渲染
- 首屏慢、SEO 差

Next.js 15:
- 默认 Server Component + RSC
- 服务端跑完组件 → 序列化 RSC Payload → 浏览器
- 浏览器只 hydrate Client Component
- 首屏快、bundle 小

关键术语:Server Components、Client Components、RSC Payload、Streaming SSR、Suspense。


❓ 题 2:Server Component 和 Client Component 的区别?什么场景用什么?

答题框架

维度Server ComponentClient Component
声明默认"use client"
能否直连 DB❌(不安全)
能否 useState
打包服务端跑完即丢发到浏览器 + hydrate

原则能 Server 就别 Client。只在需要交互的最小组件上加 "use client"


❓ 题 3:解释 RSC Payload。SSR 的 HTML 和 RSC 的 Payload 有什么不同?

答题框架

  • SSR HTML<div>...</div>,浏览器直接渲染
  • RSC Payload:类似 JSON 的特殊格式,包含组件树结构 + 已序列化的 props + Client Component 引用

关键:RSC Payload 是中间表示,浏览器解析后既能渲染 HTML,又能识别 Client Component 边界。


❓ 题 4:Next.js 14/15 引入的 Server Actions 是什么?和 API Route 的区别?

答题框架

  • API Route:手写 app/api/.../route.ts,客户端用 fetch 调用
  • Server Actions"use server" 标记的函数,直接当函数调,自动处理序列化、CSRF、加密

Server Actions 优势

  • 不用写 fetch
  • 自动 CSRF 保护(只接受 POST)
  • 自动加密 action ID
  • 配合 useActionState 拿到 pending + error

❓ 题 5:App Router 相对 Pages Router 的优势?

答题框架

维度Pages RouterApp Router
布局手动嵌套文件系统嵌套 layout
默认渲染ClientServer Component
Loading手动写loading.tsx 自动
流式 SSR不支持原生 Streaming
APIpages/apiapp/api + Route Handlers

结论新项目用 App Router


❓ 题 6:什么是 Streaming SSR?解决什么问题?

答题框架

  • 传统 SSR 一次返回完整 HTML,慢的部分会阻塞整体
  • Streaming SSR 用 renderToPipeableStream先把外层 HTML 推给浏览器,慢的部分用 Suspense 兜底
  • 配合 loading.tsx用户立刻能看到骨架屏

关键 APIonShellReady 立刻 flush,onAllReady 等待所有 Suspense。


二、架构设计题

❓ 题 7:如何设计一个「商品详情页」?SSG / SSR / ISR / RSC 怎么选?

答题框架

Q1: 商品数量级?
├─ 千级(精品电商) → SSG,构建时全量生成
├─ 万级 → ISR + 预生成热门
└─ 百万级(大平台) → ISR + 动态参数 + on-demand revalidate

Q2: 价格 / 库存是否实时?
├─ 不要求实时(缓存分钟级 OK) → ISR
└─ 要求实时(不能有延迟) → SSR

最终方案(典型电商)

// app/products/[id]/page.tsx
export async function generateStaticParams() {
// 预生成热门 1000 个
const hot = await db.product.findMany({ orderBy: { sales: "desc" }, take: 1000 });
return hot.map(p => ({ id: String(p.id) }));
}

export const revalidate = 60; // 60s 增量重新验证
export const dynamicParams = true; // 其他动态参数也走 ISR

export default async function ProductPage({ params }) {
const { id } = await params;
const product = await db.product.findUnique({ where: { id } });
return <ProductDetail product={product} />;
}

❓ 题 8:怎么优化 Next.js 首屏加载速度?

答题框架(五步法)

  1. 度量:Lighthouse / Vercel Analytics / Web Vitals
  2. 减少 JS 体积
    • 默认 Server Component(零 JS 下发
    • 动态 import 大组件:const Chart = dynamic(() => import("./Chart"))
    • shadcn/ui 按需复制(不引整个库)
  3. 静态化
    • 静态内容走 SSG / ISR
    • 客户端组件用 next/dynamic 懒加载
  4. 资源优化
    • next/image 自动优化(webp、懒加载、srcset)
    • next/font 自托管字体(避免 FOIT/FOUT)
  5. 缓存
    • CDN 缓存静态页面
    • Server Action 加 idempotency 防重复

加分项

  • loading.tsx + Streaming 让用户先看到骨架屏
  • 关键资源 preload / preinit
  • 第三方脚本用 next/scriptlazyOnload 策略

❓ 题 9:RSC 的边界规则是什么?为什么函数不能传给 Client Component?

答题框架

边界规则

  1. Server Component 可 import Client Component(传 props)
  2. Client Component 不能 import Server Component
  3. 未声明 "use client" 的组件是通用组件,可被两种父组件消费
  4. 跨边界传 props 必须是可序列化

为什么函数不能传?

RSC Payload 是静态序列化格式(类似 JSON),函数没有序列化表示。如果允许传函数:

  • 服务端无法把函数体编码到 Payload
  • 客户端拿到函数引用也无法执行(代码不在客户端)
  • 安全风险(客户端执行服务端函数 = 任意代码执行)

例外:Server Actions 是个特殊通道(经过加密 ID 映射),其他函数都不行。


❓ 题 10:Server Actions 在生产环境如何保证安全?

答题框架

  1. Action ID 加密:每个 action 编译时生成唯一 ID,不暴露函数体(用户抓包看不到代码)
  2. POST-only:Server Actions 只接受 POST 请求(天然防 CSRF 探测)
  3. SameSite cookies:浏览器自动不发送第三方 cookie
  4. Origin 校验:Next.js 校验 Origin 头,不匹配直接拒绝
  5. 参数校验必须配 Zod 校验(攻击者改 formData 没商量)

最佳实践

"use server";
import { z } from "zod";

const Schema = z.object({
name: z.string().min(1).max(50),
email: z.string().email(),
});

export async function updateProfile(formData: FormData) {
const parsed = Schema.safeParse(Object.fromEntries(formData));
if (!parsed.success) return { error: parsed.error.flatten() };
// ... 真正更新逻辑
}

三、场景综合题

❓ 题 11:电商秒杀页面怎么设计?

场景:1000 件商品,10 万人同时抢购,要求不超卖、响应快。

答题框架

1. 渲染层(Next.js):
- 商品页用 SSG/ISR 静态化
- 抢购按钮实时库存用 Client Component 拉(轮询 / SSE)
- Loading + Streaming 减少等待

2. Server Action 处理下单:
- 校验登录态(cookie)
- 调用后端服务的库存扣减 API(不直连 DB)
- 幂等性:传 idempotency key 防重复

3. 服务端设计(不在 Next.js 范畴):
- Redis 预扣减(DECR 原子操作)
- MQ 异步生成订单
- 前端只轮询订单状态

Next.js 角度:用 RSC 静态化商品页 + Server Actions 提交订单 + Client Component 轮询状态。


❓ 题 12:博客系统怎么设计评论功能?

答题框架

// 评论组件 —— 用 Server Actions 提交
// app/comments/CommentForm.tsx
"use client";
import { useActionState } from "react";
import { submitComment } from "@/app/actions/comment";

export function CommentForm({ postId }) {
const [state, action, isPending] = useActionState(
async (prev, fd) => {
const result = await submitComment(postId, fd);
return result ?? prev;
},
null,
);

return (
<form action={action}>
<textarea name="content" required />
<button disabled={isPending}>{isPending ? "提交中..." : "发布"}</button>
{state?.error && <p>{state.error}</p>}
</form>
);
}

// app/actions/comment.ts
"use server";
import { revalidatePath } from "next/cache";

export async function submitComment(postId, formData) {
const content = formData.get("content");
if (!content) return { error: "内容不能为空" };
await db.comment.create({ data: { postId, content } });
revalidatePath(`/posts/${postId}`); // ← 关键:刷新评论列表
}

关键点

  • Server Actions 提交
  • revalidatePath 让评论列表自动刷新
  • 客户端拿到 isPending 做 loading

❓ 题 13:怎么实现「登录后自动跳回原页面」?

答题框架

// middleware.ts
import { NextResponse } from "next/server";

export function middleware(req) {
const token = req.cookies.get("token");
const isAuthed = !!token;

const isLoginPage = req.nextUrl.pathname === "/login";
const protectedPath = req.nextUrl.pathname.startsWith("/dashboard");

if (protectedPath && !isAuthed) {
// 关键:把当前 URL 编码到 redirect 参数
const url = new URL("/login", req.url);
url.searchParams.set("redirect", req.nextUrl.pathname);
return NextResponse.redirect(url);
}

return NextResponse.next();
}

// app/login/page.tsx
"use client";
import { useSearchParams, useRouter } from "next/navigation";

export default function LoginPage() {
const searchParams = useSearchParams();
const router = useRouter();

async function handleLogin() {
await login();
// 跳回原页面,没有就回首页
const redirect = searchParams.get("redirect") || "/";
router.push(redirect);
}

return <button onClick={handleLogin}>登录</button>;
}

❓ 题 14:怎么排查 Next.js 应用 Hydration mismatch 错误?

答题框架

常见原因

  1. 服务端 / 客户端渲染结果不一致
    • Date.now() / Math.random()
    • window / document 引用
    • 时间格式(locale 不同)
  2. 浏览器扩展(翻译插件、广告拦截)改了 DOM
  3. HTML 不合法(如 <p> 嵌套 <div>

排查方法

// 1. 用 suppressHydrationWarning 暂时屏蔽(不推荐长期使用)
<div suppressHydrationWarning>{new Date().toLocaleString()}</div>

// 2. 拆分动态内容到 Client Component
"use client";
function ClientOnlyTime() {
const [time, setTime] = useState<string>("");
useEffect(() => {
setTime(new Date().toLocaleString());
}, []);
return <div>{time}</div>;
}

// 3. 用 useId 替代随机 id
const id = useId(); // SSR / CSR 唯一且一致

❓ 题 15:Next.js 应用怎么实现「国际化」?

答题框架

方案 1:路由级 i18n(推荐)

app/
├─ [locale]/
│ ├─ layout.tsx
│ ├─ page.tsx
│ └─ about/page.tsx
// next.config.ts
const i18n = {
locales: ["zh", "en", "ja"],
defaultLocale: "zh",
};

方案 2:next-intl 库

import { useTranslations } from "next-intl";

export default function Page() {
const t = useTranslations("Home");
return <h1>{t("title")}</h1>;
}

方案 3:i18next + react-i18next(客户端方案,配合 localStorage 缓存用户偏好)。

加分项:RSC 下用 getTranslations 服务端读 messages,比客户端方案首屏更快


四、面试结尾反问

准备 2~3 个反问,展示你深入思考。

  • 你们项目用的是 App Router 还是 Pages Router?为什么?
  • RSC 在你们项目落地的最大收益是什么?踩过哪些坑?
  • 你们的 ISR 失效策略是怎么设计的?基于时间还是事件?
  • Turbopack 在你们生产 build 用过吗?稳定性如何?

五、相关文档