引言
在现代 Web 开发中,静态站点生成(SSG)已成为提升性能和 SEO 的关键技术。Next.js 推出的增量静态再生(ISR)更是革新了传统 SSG 的工作方式,让我们能够在保持静态页面性能优势的同时,实现内容的动态更新。本文将深入探讨 SSG 原理,并通过实战代码展示 ISR 的完整应用。
SSG 核心原理
SSG 在构建时预渲染页面,生成纯 HTML 文件。与传统 SSR 相比,SSG 的优势在于:
- 极致性能:无需服务端计算,CDN 直接返回 HTML
- SEO 友好:搜索引擎可直接抓取完整内容
- 成本更低:静态托管费用远低于动态服务器
但传统 SSG 有个致命缺陷:内容更新需要重新构建整个站点。ISR 通过"按需再生"解决了这个问题。
ISR 工作机制
ISR 允许为每个页面设置 revalidate 时间(秒)。当用户访问过期页面时:
- 立即返回旧的静态页面(保证响应速度)
- 后台触发重新生成
- 下次请求返回新页面
这种" stale-while-revalidate "策略兼顾了性能与时效性。
Next.js ISR 实战
基础配置
// pages/blog/[slug].js
export async function getStaticPaths() {
const posts = await fetchPosts();
return {
paths: posts.map(post => ({
params: { slug: post.slug }
})),
fallback: 'blocking' // 关键:阻塞式回退
};
}
export async function getStaticProps({ params }) {
const post = await fetchPost(params.slug);
return {
props: { post },
revalidate: 60 // 60 秒后过期,下次访问时再生
};
}
export default function BlogPost({ post }) {
return (
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</article>
);
}
On-Demand Revalidation
对于需要即时更新的场景,Next.js 支持手动触发再生:
// pages/api/revalidate.js
export default async function handler(req, res) {
// 验证请求来源(生产环境必须)
if (req.query.secret !== process.env.REVALIDATION_TOKEN) {
return res.status(401).json({ message: 'Invalid token' });
}
try {
// 重新生成指定页面
await res.revalidate('/blog/nextjs-features');
// 或重新生成所有匹配路径
// await res.revalidate('/blog');
return res.json({ revalidated: true });
} catch (err) {
return res.status(500).send('Error revalidating');
}
}
CMS 集成示例
当 CMS 发布新内容时,自动触发再生:
// CMS Webhook 处理
async function handleContentUpdate(content) {
const slug = content.slug;
// 生成新页面
await fetch(`/api/revalidate?secret=${TOKEN}&path=/blog/${slug}`);
// 更新列表页
await fetch(`/api/revalidate?secret=${TOKEN}&path=/blog`);
}
性能优化技巧
1. 增量构建
// next.config.js
module.exports = {
experimental: {
isr: {
memoryBasedWorkerCount: true, // 根据内存调整 worker 数
}
}
};
2. 智能缓存策略
// 根据内容类型设置不同 revalidate 时间
const REVALIDATE_TIMES = {
news: 60, // 新闻:1 分钟
blog: 3600, // 博客:1 小时
docs: 86400, // 文档:1 天
about: false // 关于页:永不过期
};
export async function getStaticProps({ params }) {
const type = params.type;
const content = await fetchContent(params.slug);
return {
props: { content },
revalidate: REVALIDATE_TIMES[type]
};
}
3. 回退页面优化
// components/FallbackSkeleton.js
export function BlogPostSkeleton() {
return (
<div className="animate-pulse">
<div className="h-8 bg-gray-200 rounded w-3/4 mb-4" />
<div className="h-4 bg-gray-200 rounded w-full mb-2" />
<div className="h-4 bg-gray-200 rounded w-5/6" />
</div>
);
}
// 在 getStaticPaths 中使用 fallback: 'blocking'
// 首次访问会等待生成,后续访问直接返回静态页面
部署注意事项
Vercel 部署
Vercel 原生支持 ISR,无需额外配置:
# 设置环境变量
vercel env add REVALIDATION_TOKEN production
自建部署
使用 next start 或 next-server,确保 Node 进程常驻:
# Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY . .
RUN npm ci && npm run build
EXPOSE 3000
CMD ["npm", "start"]
总结
ISR 代表了静态生成的未来方向:
| 特性 | 传统 SSG | ISR | SSR |
|---|---|---|---|
| 首屏速度 | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ |
| 内容时效 | ⭐ | ⭐⭐⭐ | ⭐⭐⭐ |
| 服务器成本 | ⭐⭐⭐ | ⭐⭐⭐ | ⭐ |
| 实现复杂度 | ⭐⭐ | ⭐⭐ | ⭐ |
对于内容型网站,ISR 是最佳选择。它让我们既能享受静态页面的性能红利,又能保持内容的及时更新。掌握 ISR,是现代前端工程师的必备技能。