SSR vs SSG 比较: 用 Claude Code 选择 Next.js/Astro 渲染策略
用 Claude Code 对比 Next.js/Astro 的 SSR、SSG、ISR 与静态导出,含可运行代码、验证命令和设计提示词。
先统一概念
SSR 和 SSG 的选择,不只是“哪个更快”。SSR 是 server-side rendering,也就是用户请求到达时,由服务器生成 HTML。SSG 是 static site generation,也就是构建阶段先生成 HTML,再交给 CDN 或静态主机分发。ISR 是 Incremental Static Regeneration,可以先按静态页面分发,再按时间或事件重新生成。
新手常见误解是:Next.js App Router 里的 Server Component 并不等于每次请求都 SSR。当前官方 Dynamic Route Segments 的 TypeScript 示例把 params 写成 Promise。如果页面没有使用常见的请求时 API,但你希望明确等到真实请求再渲染,可以使用官方 connection。
如果只让 Claude Code “把页面变快”,它可能把页面静态化得太多,导致库存、搜索结果或会员信息过期。如果全部改成 SSR,又可能增加 TTFB 和服务器费用。更稳妥的做法,是先让 Claude Code 输出路由矩阵:数据新鲜度、是否个性化、更新频率、变现页面影响、验证命令。
对比表与真实场景
| 问题 | SSG | ISR | SSR |
|---|---|---|---|
| HTML 生成时机 | 构建阶段 | 构建与重新验证阶段 | 请求到达时 |
| 适合页面 | 文章、文档、落地页 | 商品、分类、新闻列表 | 仪表盘、搜索、购物车 |
| 数据新鲜度 | 构建时快照 | 按时间或事件更新 | 接近实时 |
| 分发成本 | 低 | 中等 | 通常更高 |
| 常见问题 | 构建变长、内容过期 | 短时间内可能看到旧内容 | 不缓存会变慢 |
场景一是博客或文档。正文、OGP、内部链接和 CTA 在发布时固定,就优先选 SSG。文章修改时重新构建即可。如果还要减少 JavaScript 体积,可以结合Claude Code 代码分割指南。
场景二是电商商品页。价格、库存、分类排序可能几分钟更新一次,但访问者仍然需要很快的静态响应。ISR 更适合这种页面。Next.js 官方 ISR 指南说明,ISR 支持 Node.js runtime,但不支持静态导出。
场景三是会员仪表盘。Cookie、权限、账单状态、未读通知、私人推荐都和请求有关,应该按 SSR 设计,并明确缓存头。若团队还在评估边缘部署,可以在策略确定后阅读Claude Code 边缘计算指南。
Next.js 的 SSR 示例
下面的例子可以直接放进 Next.js App Router 项目。它使用 dummyjson.com,不需要 API key。connection() 和 cache: "no-store" 一起出现,是为了让评审时明确知道这个页面按请求渲染。
// app/products/[id]/page.tsx
import { notFound } from "next/navigation";
import { connection } from "next/server";
type Product = {
id: number;
title: string;
price: number;
stock: number;
updatedAt: string;
};
async function getProduct(id: string): Promise<Product | null> {
await connection();
const res = await fetch(`https://dummyjson.com/products/${id}`, {
cache: "no-store",
});
if (res.status === 404) return null;
if (!res.ok) throw new Error(`Failed to load product: ${res.status}`);
const data = (await res.json()) as {
id: number;
title: string;
price: number;
stock: number;
meta?: { updatedAt?: string };
};
return {
id: data.id,
title: data.title,
price: data.price,
stock: data.stock,
updatedAt: data.meta?.updatedAt ?? new Date().toISOString(),
};
}
export default async function ProductPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
const product = await getProduct(id);
if (!product) notFound();
return (
<main>
<h1>{product.title}</h1>
<p>Price: ${product.price.toLocaleString("en-US")}</p>
<p>Stock: {product.stock}</p>
<p>Updated: {new Date(product.updatedAt).toLocaleString("zh-CN")}</p>
</main>
);
}
这里最容易踩的坑,是把 cookies() 或 headers() 放进共享 layout。它看起来只是取主题或地区,实际却可能让一组页面变成动态渲染。如果这个 layout 包住文章页,本来可静态分发的页面也会受影响。让 Claude Code 在改动前列出受影响路由,比事后看性能报告更可靠。
区分 SSG、ISR 与静态导出
SSG 和 ISR 对访问者都像静态页面,但运维方式不同。SSG 在构建时生成,ISR 在时间窗口或事件后重新生成。完全静态导出则通过 output: "export" 生成 out 目录,不需要 Node.js 服务器。官方 Static Exports 说明,next build 可以为每个路由生成 HTML 文件并用于静态托管。
// app/catalog/[id]/page.tsx
import { notFound } from "next/navigation";
export const revalidate = 3600;
type Product = {
id: number;
title: string;
description: string;
};
export async function generateStaticParams() {
return ["1", "2", "3"].map((id) => ({ id }));
}
async function getProduct(id: string): Promise<Product | null> {
const res = await fetch(`https://dummyjson.com/products/${id}`, {
next: { revalidate: 3600, tags: [`product:${id}`] },
});
if (res.status === 404) return null;
if (!res.ok) throw new Error(`Failed to load product: ${res.status}`);
return (await res.json()) as Product;
}
export default async function CatalogPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
const product = await getProduct(id);
if (!product) notFound();
return (
<article>
<h1>{product.title}</h1>
<p>{product.description}</p>
</article>
);
}
// next.config.mjs
const nextConfig = {
output: "export",
images: {
unoptimized: true,
},
};
export default nextConfig;
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"export:check": "next build && npx serve out"
}
}
不要把静态导出和 ISR 当成可以随意混用的方案。静态导出适合纯静态托管。如果需要 Cookie、按需重新验证、Server Function 或真实 SSR,就要准备服务器运行时。给 Claude Code 的任务里应先写清楚部署目标:Cloudflare Pages 静态托管、Node 容器、Vercel、Netlify,还是其他平台。
Astro 的静态与按需渲染
Astro 默认偏向静态生成。官方 Astro on-demand rendering 文档说明,页面、路由和 API endpoint 默认在构建阶段预渲染;安装对应 adapter 后,可以对单个路由写 export const prerender = false,让它按请求渲染。
---
// src/pages/docs/[slug].astro
export async function getStaticPaths() {
const docs = [
{ slug: "ssr", title: "SSR guide" },
{ slug: "ssg", title: "SSG guide" },
];
return docs.map((doc) => ({
params: { slug: doc.slug },
props: { doc },
}));
}
const { doc } = Astro.props;
---
<html lang="zh-CN">
<body>
<article>
<h1>{doc.title}</h1>
<p>This page was generated at build time.</p>
</article>
</body>
</html>
---
// src/pages/account.astro
export const prerender = false;
const session = Astro.cookies.get("session")?.value;
const name = session ? "Masa" : "Guest";
Astro.response.headers.set("Cache-Control", "private, no-store");
---
<html lang="zh-CN">
<body>
<h1>Account</h1>
<p>Hello, {name}.</p>
</body>
</html>
Astro 的常见问题是忘记 adapter。页面在本地看起来正常,但生产环境的按需渲染需要服务器运行时。让 Claude Code 检查 astro.config.mjs、部署平台和每个 prerender 覆盖项,可以减少上线后的异常。
验证命令与 Claude Code 提示词
渲染策略不能只靠读代码确认。要看构建结果、响应头、缓存日志和性能指标。Next.js ISR 官方也建议通过 next build 和 next start 验证生产行为。
# Next.js: production behavior
npm run build
npm run start
# In another terminal
curl -I http://localhost:3000/catalog/1
curl -I http://localhost:3000/products/1
# ISR cache debugging
NEXT_PRIVATE_DEBUG_CACHE=1 npm run start
# Astro
npm run build
npm run preview
给 Claude Code 的提示词不要只要求代码,要要求判断记录:
Goal: classify every route in a Next.js/Astro site as SSR, SSG, ISR, or static export.
Inspect:
- app/ or src/pages/ routes
- cookies(), headers(), searchParams, connection(), cache: "no-store"
- generateStaticParams(), revalidate, output: "export", Astro prerender
- monetization pages, member pages, articles, product pages, and search pages
Constraints:
- Do not break SEO metadata or internal links
- Keep npm run build passing before and after
- Return a table with the chosen strategy, reason, touched files, and verification command
具体坑点
第一,把用户专属内容放进静态文章。会员名、私人推荐、隐藏价格不应写入 SSG HTML。文章正文保持静态,个性化部分放到客户端组件或明确的动态区域。
第二,把 ISR 时间设得太短。revalidate = 1 往往只是把成本重新交给服务器。需要精确更新时用按需重新验证,需要实时数据时用 SSR。
第三,选择静态导出后又补充服务器能力。认证、Cookie UI、Server Route Handler、ISR 都需要运行时。静态托管很好,但不能替代服务器。
第四,只因为 Claude Code 的方案“看起来最快”就采用。对变现内容站来说,性能只是质量的一部分。OGP、结构化数据、内部链接、广告位置、CTA 计量、LCP、CLS 都要看。渲染策略确定后,可以用Claude Code 性能优化继续检查。
结合变现的建议
ClaudeCodeLab 这类内容业务,默认应把文章、教程、比较页和漏斗入口做成 SSG。商品目录、分类页、新闻式列表用 ISR。会员页、搜索、购物车、结账上下文和任何依赖私人请求数据的页面用 SSR。
如果想先建立日常流程,可以领取免费 Claude Code 速查表。需要模板和实用包,可以查看产品页面。如果团队要一起整理路由策略、权限、代码评审、分析与上线训练,可以从Claude Code 培训与咨询开始。
实际验证结果
这次更新时,我核对了 Next.js Dynamic Route Segments、connection、ISR、Static Exports、Astro on-demand rendering 和 Claude Code overview。Masa 的内容运营经验里,文章用 SSG、商品和分类列表用 ISR、账号和搜索用 SSR 是最稳定的分法。最有用的 Claude Code 步骤不是马上改代码,而是先生成路由分类表。它能提前暴露静态页面误用 Cookie 的问题,也让每个路由的理由和验证命令在评审时一眼可见。
免费 PDF: Claude Code 速查表
输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。
我们会妥善保护你的信息,不发送垃圾邮件。
把 Claude Code 变成真正能带来结果的工作流
先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。
关于作者
Masa
专注 Claude Code 实务流程、团队导入和内容转化的工程师。