静态生成 SSG:ISR 增量静态化实战

引言

在现代 Web 开发中,静态站点生成(SSG)已成为提升性能和 SEO 的关键技术。Next.js 推出的增量静态再生(ISR)更是革新了传统 SSG 的工作方式,让我们能够在保持静态页面性能优势的同时,实现内容的动态更新。本文将深入探讨 SSG 原理,并通过实战代码展示 ISR 的完整应用。

SSG 核心原理

SSG 在构建时预渲染页面,生成纯 HTML 文件。与传统 SSR 相比,SSG 的优势在于:

  • 极致性能:无需服务端计算,CDN 直接返回 HTML
  • SEO 友好:搜索引擎可直接抓取完整内容
  • 成本更低:静态托管费用远低于动态服务器

但传统 SSG 有个致命缺陷:内容更新需要重新构建整个站点。ISR 通过"按需再生"解决了这个问题。

ISR 工作机制

ISR 允许为每个页面设置 revalidate 时间(秒)。当用户访问过期页面时:

  1. 立即返回旧的静态页面(保证响应速度)
  2. 后台触发重新生成
  3. 下次请求返回新页面

这种" stale-while-revalidate "策略兼顾了性能与时效性。

Next.js ISR 实战

基础配置

javascript 复制代码
// 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 支持手动触发再生:

javascript 复制代码
// 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 发布新内容时,自动触发再生:

javascript 复制代码
// 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. 增量构建

java 复制代码
// next.config.js
module.exports = {
  experimental: {
    isr: {
      memoryBasedWorkerCount: true, // 根据内存调整 worker 数
    }
  }
};

2. 智能缓存策略

typescript 复制代码
// 根据内容类型设置不同 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. 回退页面优化

javascript 复制代码
// 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,无需额外配置:

bash 复制代码
# 设置环境变量
vercel env add REVALIDATION_TOKEN production

自建部署

使用 next startnext-server,确保 Node 进程常驻:

bash 复制代码
# 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,是现代前端工程师的必备技能。

相关推荐
原则猫16 分钟前
await 到底在等待什么
前端
西洼工作室25 分钟前
fetch+ReadableStream实现SSE推送实时踢人下线
前端·python·全栈
农夫山泉不太甜31 分钟前
Nuxt 4 完全指南:从入门到精通
前端
Momo__33 分钟前
Vue 3.4+ 被低估的 3 个 API,让你的代码更优雅
前端·vue.js
dishugj36 分钟前
HANA数据库常用命令总结
java·前端·数据库
clove40 分钟前
JavaScript 提升(Hoisting)与声明优先级:一篇文章说透
前端
七牛开发者44 分钟前
不写框架、不用 npm,我用 AI Coding 做了一个家庭记忆站
前端·人工智能·npm
@PHARAOH1 小时前
WHAT - npm和corepack
前端·npm·node.js
不爱学英文的码字机器1 小时前
被 AE 的关键帧折磨过的人,应该试试这个用 React 写视频的路子
前端·react.js·音视频