下面,我们来系统的梳理关于 React 静态站点生成(SSG) 的基本知识点:


一、静态站点生成(SSG)概述

1.1 什么是静态站点生成?

静态站点生成(Static Site Generation, SSG)是一种在构建时生成完整HTML页面的技术。与传统的服务器端渲染(SSR)不同,SSG在应用部署前预先生成所有页面,并将它们作为静态文件提供给用户。

1.2 SSG的核心优势

优势 说明
极高性能 预生成的HTML文件可直接从CDN提供,加载速度快
高安全性 无服务器运行时,减少攻击面
成本效益 无需服务器基础设施,托管成本低
SEO友好 搜索引擎可轻松抓取完整内容
开发体验佳 使用现代前端工具链,支持组件化开发

1.3 SSG与传统渲染方式对比

特性 SSG SSR CSR
渲染时机 构建时 请求时 客户端
性能 ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐
SEO ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
动态内容 ⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
服务器需求 必需
适用场景 内容为主 个性化内容 Web应用

二、Next.js 中的 SSG 实现

2.1 核心API:getStaticProps

export async function getStaticProps(context) {
  // 获取数据逻辑
  const data = await fetchData();
  
  return {
    props: { data },        // 传递给页面的props
    revalidate: 60,         // 增量静态再生时间(秒)
    notFound: false,        // 是否返回404
    redirect: {             // 重定向配置
      destination: '/',
      permanent: false
    }
  };
}

2.2 动态路由:getStaticPaths

export async function getStaticPaths() {
  // 获取所有可能的路径参数
  const products = await getAllProducts();
  
  return {
    paths: products.map(product => ({
      params: { id: product.id }
    })),
    fallback: 'blocking' // 处理未预生成的路径
  };
}

2.3 增量静态再生(ISR)

Next.js 9.5+ 引入的革命性功能:

export async function getStaticProps({ params }) {
  const product = await getProduct(params.id);
  
  return {
    props: { product },
    revalidate: 3600 // 每1小时重新验证
  };
}

ISR工作流程

用户A CDN 用户B Next.js 用户C 请求页面 返回缓存的HTML 请求页面(超过revalidate时间) 转发请求 返回旧页面 + 后台重新生成 更新缓存 请求页面 返回新生成的HTML 用户A CDN 用户B Next.js 用户C

三、SSG 使用场景与实践

3.1 典型应用场景

  1. 内容型网站:博客、文档、新闻站点
  2. 电商平台:产品目录、商品详情页
  3. 营销页面:企业官网、产品落地页
  4. 文档系统:技术文档、帮助中心
  5. 作品集网站:设计师、开发者作品展示

3.2 内容管理集成

Headless CMS 架构

内容编辑
Headless CMS
Next.js 构建
静态文件
CDN分发

常用Headless CMS

  • Sanity
  • Contentful
  • Strapi
  • Prismic
  • WordPress (Headless模式)

3.3 性能优化策略

  1. 图片优化
import Image from 'next/image';

<Image
  src="/product.jpg"
  alt="Product"
  width={800}
  height={600}
  quality={85}
  placeholder="blur"
  blurDataURL="data:image/jpeg;base64,..."
/>
  1. 代码分割
import dynamic from 'next/dynamic';

const CommentsSection = dynamic(
  () => import('../components/Comments'),
  { ssr: false, loading: () => <Spinner /> }
);
  1. 预取链接
<Link href="/blog/[slug]" as={`/blog/${post.slug}`} prefetch>
  <a>{post.title}</a>
</Link>

3.4 SEO最佳实践

  1. 结构化数据
import Head from 'next/head';

function ProductPage({ product }) {
  return (
    <>
      <Head>
        <script type="application/ld+json">
          {JSON.stringify({
            "@context": "https://schema.org/",
            "@type": "Product",
            "name": product.name,
            "description": product.description,
            "image": product.image
          })}
        </script>
      </Head>
      {/* 页面内容 */}
    </>
  );
}
  1. 动态元数据
export async function getStaticProps() {
  return {
    props: {
      meta: {
        title: "产品详情",
        description: "了解我们的优质产品",
        canonical: "https://example.com/products"
      }
    }
  };
}

function ProductPage({ meta }) {
  return (
    <>
      <Head>
        <title>{meta.title}</title>
        <meta name="description" content={meta.description} />
        <link rel="canonical" href={meta.canonical} />
      </Head>
      {/* 页面内容 */}
    </>
  );
}

四、高级SSG模式

4.1 混合渲染模式

Next.js 允许在同一个应用中混合使用SSG、SSR和CSR:

SSG
SSG+ISR
SSR
CSR
首页
预渲染
博客文章
用户仪表盘
服务器渲染
实时聊天
客户端渲染

4.2 按需静态生成

使用 revalidate 实现准实时更新:

// 在API路由中触发重新验证
// pages/api/revalidate.js
export default async function handler(req, res) {
  // 验证secret确保安全
  if (req.query.secret !== process.env.REVALIDATE_SECRET) {
    return res.status(401).json({ message: 'Invalid token' });
  }
  
  try {
    await res.revalidate('/products');
    return res.json({ revalidated: true });
  } catch (err) {
    return res.status(500).send('Error revalidating');
  }
}

4.3 内容预取策略

// 在用户交互时预取页面
const handleProductHover = (productId) => {
  import('next/link').then(({ default: Link }) => {
    const href = `/products/${productId}`;
    const as = href;
    // 预取页面
    const router = require('next/router').default;
    router.prefetch(href, as);
  });
};

五、实战:构建SSG博客系统

5.1 项目结构

├── pages/
│   ├── index.js       # 博客首页
│   ├── [slug].js      # 博客文章
│   └── api/
│       └── revalidate.js # 重新验证API
├── lib/
│   ├── markdown.js    # Markdown处理
│   └── api.js         # CMS API客户端
└── components/
    ├── Layout.js
    ├── Article.js
    └── TableOfContents.js

5.2 首页实现

// pages/index.js
export async function getStaticProps() {
  const posts = await getAllPosts({
    fields: ['slug', 'title', 'date', 'excerpt']
  });
  
  return {
    props: { posts },
    revalidate: 60 // 每分钟重新验证
  };
}

function HomePage({ posts }) {
  return (
    <Layout>
      <h1>最新文章</h1>
      <ul className="post-list">
        {posts.map(post => (
          <li key={post.slug}>
            <article>
              <h2>
                <Link href={`/${post.slug}`}>
                  <a>{post.title}</a>
                </Link>
              </h2>
              <time>{formatDate(post.date)}</time>
              <p>{post.excerpt}</p>
            </article>
          </li>
        ))}
      </ul>
    </Layout>
  );
}

5.3 文章页面

// pages/[slug].js
export async function getStaticPaths() {
  const posts = await getAllSlugs();
  
  return {
    paths: posts.map(post => ({ params: { slug: post.slug } })),
    fallback: 'blocking'
  };
}

export async function getStaticProps({ params }) {
  const post = await getPostBySlug(params.slug, [
    'title',
    'date',
    'content',
    'toc'
  ]);
  
  if (!post) {
    return { notFound: true };
  }
  
  // 将Markdown转换为HTML
  const content = await markdownToHtml(post.content || '');
  
  return {
    props: {
      post: {
        ...post,
        content
      }
    },
    revalidate: 86400 // 每天重新验证
  };
}

function BlogPost({ post }) {
  return (
    <Layout>
      <article>
        <header>
          <h1>{post.title}</h1>
          <time>{formatDate(post.date)}</time>
        </header>
        
        <div className="content">
          {post.toc && <TableOfContents items={post.toc} />}
          <div dangerouslySetInnerHTML={{ __html: post.content }} />
        </div>
      </article>
    </Layout>
  );
}

5.4 内容更新钩子

// pages/api/revalidate.js
export default async function handler(req, res) {
  // 验证Webhook签名
  if (req.headers['x-webhook-secret'] !== process.env.WEBHOOK_SECRET) {
    return res.status(401).json({ message: 'Invalid signature' });
  }
  
  try {
    const { event, data } = req.body;
    
    // 重新生成首页
    await res.revalidate('/');
    
    // 重新生成更改的文章
    if (event === 'post.updated' && data.slug) {
      await res.revalidate(`/${data.slug}`);
    }
    
    return res.json({ revalidated: true });
  } catch (err) {
    console.error('Revalidation error:', err);
    return res.status(500).send('Error revalidating');
  }
}

六、部署与优化

6.1 部署平台选择

平台 特点 SSG支持
Vercel Next.js官方平台,自动ISR ⭐⭐⭐⭐⭐
Netlify 强大插件系统 ⭐⭐⭐⭐
AWS Amplify 云服务集成 ⭐⭐⭐⭐
GitHub Pages 免费,简单 ⭐⭐

6.2 性能监测

// 使用Web Vitals
import { reportWebVitals } from 'next/web-vitals';

export function reportWebVitals(metric) {
  // 发送到分析平台
  analytics.send(metric);
}

// next.config.js
module.exports = {
  experimental: {
    optimizeFonts: true,
    optimizeCss: true,
    optimizeImages: true,
  },
};

6.3 缓存策略

推荐CDN配置

Cache-Control: public, max-age=31536000, immutable

Next.js资源缓存

// next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/_next/static/:path*',
        headers: [
          {
            key: 'Cache-Control',
            value: 'public, max-age=31536000, immutable',
          },
        ],
      },
      {
        source: '/static/:path*',
        headers: [
          {
            key: 'Cache-Control',
            value: 'public, max-age=31536000, immutable',
          },
        ],
      },
    ];
  },
};

七、常见问题与解决方案

7.1 构建时间过长

解决方案

  1. 增量静态生成(ISR)
  2. 按需生成页面
  3. 并行构建
  4. 优化数据查询

7.2 动态内容更新

解决方案

  1. 使用revalidate进行增量再生
  2. 通过API路由手动触发重新验证
  3. 客户端获取动态部分

7.3 大型网站路径生成

解决方案

export async function getStaticPaths() {
  // 只生成热门路径
  const popularPaths = await getPopularPaths(1000);
  
  return {
    paths: popularPaths,
    fallback: 'blocking'
  };
}

八、SSG生态系统

8.1 框架选择

框架 特点 适用场景
Next.js 最流行的React框架 全功能应用
Gatsby 强大的数据层 内容密集型
Astro 岛屿架构 内容为主+交互组件
Eleventy 简单灵活 内容为中心

8.2 相关工具

  • CMS集成:Sanity, Contentful, Strapi
  • 部署平台:Vercel, Netlify, AWS Amplify
  • 分析工具:Google Analytics, Plausible, Fathom
  • 性能监测:Lighthouse, Web Vitals, SpeedCurve

九、总结

SSG核心价值矩阵

维度 价值
性能 即时加载,CDN分发
安全 无服务器暴露面
扩展性 自动CDN扩展
开发者体验 现代工具链,快速迭代
成本 静态托管成本极低

技术选型决策树

低频
中频
高频
需要完全静态内容?
使用纯SSG
内容更新频率?
SSG + 手动重新构建
SSG + ISR
需要个性化?
SSR或CSR
SSG + 客户端数据获取
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐