🚧 Rspress 2.0 文档还在开发中
close

静态站点生成(SSG)

什么是 SSG

SSG(Static Site Generation,静态站点生成)是指在构建阶段将页面预渲染为 HTML 文件,而不是在用户访问时才进行渲染。

SSG 的优势:

  • 首屏渲染更快:用户无需等待 JavaScript 加载和执行,浏览器加载 HTML 后即可看到完整内容
  • SEO 友好:搜索引擎爬虫可以直接抓取到完整的 HTML 内容
  • 易于部署:产物是纯静态文件,无需部署服务器 API,可直接上传到 CDN 或任何静态托管服务。

Rspress 默认启用 SSG,这意味着当你执行 rspress build 时,每个页面都会被预渲染为包含完整内容的 HTML 文件。以下是关于 SSG 实现的详细说明,旨在帮助你更深入地理解其工作原理。

dev 和 build 下的区别

Rspress 在不同的运行模式下采用不同的渲染策略:在开发阶段使用客户端渲染以保证开发体验,而在生产构建阶段则默认开启 SSG 以优化性能。

对比项dev 模式 (开发)build 模式 (生产)
命令rspress devrspress build
渲染方式纯 CSR(客户端渲染)SSG(默认)或 CSR
预渲染 HTMLSSG 时预渲染所有页面
适用场景开发调试、热更新产物性能、SEO
预览方式直接访问开发服务器rspress preview

dev 模式

rspress dev

dev 模式为了获得更快的开发体验和热更新能力,使用纯客户端渲染(CSR),不进行 HTML 预渲染。

提示

如果你的代码在 dev 模式下正常运行,但在 build 模式下报错,这通常是因为 SSG 在 Node.js 环境中渲染,无法访问浏览器 API(如 windowdocument)。请参考下文的「常见问题与解决方案」

build 模式

rspress build

build 模式默认启用 SSG,你可以通过 ssg 配置项来控制:

  • ssg: true (默认):启用 SSG,构建时,Rspress 会在 Node.js 环境中执行 React 组件的渲染,将每个页面转换为包含完整内容的 HTML 文件。
  • ssg: false:禁用 SSG,使用纯 CSR,生成的 HTML 仅包含空容器,等待客户端渲染。

构建完成后:

  • 本地预览:使用 rspress preview 启动本地静态服务器预览产物

    rspress preview
  • 服务器部署:将 doc_build 目录部署到静态托管服务(GitHub Pages、Netlify、Vercel 等)

SSG 与 CSR 产物的对比

产物目录结构

无论是 SSG 还是 CSR 模式,产物的目录结构是相同的:

doc_build
static
js
main.[hash].js
async
css
main.[hash].css
index.html
404.html
guide
getting-started.html
api
config.html

其中 404.html 是 Rspress 自动生成的 404 页面,用于处理不存在的路由。该文件在 SPA 部署时有重要作用,详见「刷新页面时出现 404」

HTML 内容差异

两种模式的核心区别在于 HTML 文件的内容:

SSG 产物 HTML(包含预渲染的完整内容):

<body>
  <div id="__rspress_root">
    <!-- 预渲染的完整页面内容 -->
    <nav>...</nav>
    <main>
      <article>
        <h1>Getting Started</h1>
        <p>Welcome to Rspress...</p>
      </article>
    </main>
  </div>
  <script src="/static/js/main.[hash].js"></script>
</body>

CSR 产物的 HTML(只有空容器,等待 JS 渲染):

<body>
  <div id="__rspress_root"></div>
  <script src="/static/js/main.[hash].js"></script>
</body>

加载流程差异

SSG 加载流程:

  1. 浏览器加载 HTML → 用户立即看到完整内容
  2. JavaScript 加载完成 → React hydrate(水合),绑定事件交互
  3. 后续页面导航 → SPA 模式,客户端渲染

CSR 加载流程:

  1. 浏览器加载 HTML → 用户看到空白页面
  2. JavaScript 加载完成 → React 渲染页面内容
  3. 后续页面导航 → SPA 模式,客户端渲染

常见问题与解决方案

window is not defined / document is not defined

原因:SSG 在 Node.js 环境中渲染页面,而 windowdocument 等是浏览器特有的全局对象,在 Node.js 中不存在。

解决方案

  1. 使用 useEffect 延迟执行:将浏览器 API 的调用放在 useEffect 中,确保只在客户端执行

    import { useEffect, useState } from 'react';
    
    function MyComponent() {
      const [width, setWidth] = useState(0);
    
      useEffect(() => {
        // 只在客户端执行
        setWidth(window.innerWidth);
      }, []);
    
      return <div>Window width: {width}</div>;
    }
  2. 条件检查:在访问浏览器 API 前检查环境

    if (typeof window !== 'undefined') {
      // 浏览器环境
      console.log(window.location.href);
    }
  3. 动态导入:对于依赖浏览器 API 的第三方库,使用动态导入

    import { useEffect, useState } from 'react';
    
    function MyComponent() {
      const [Editor, setEditor] = useState(null);
    
      useEffect(() => {
        import('some-browser-only-library').then((mod) => {
          setEditor(() => mod.default);
        });
      }, []);
    
      if (!Editor) return <div>Loading...</div>;
      return <Editor />;
    }

Hydration mismatch

原因:服务器渲染的 HTML 内容与客户端首次渲染的内容不一致。React 在 hydration 时会检测两者是否匹配,不匹配会导致警告或错误。

常见场景

  • 使用了 Date.now() 或随机数
  • 根据 window 对象的属性(如 window.innerWidth)渲染不同内容
  • 使用了只在客户端存在的数据(如 localStorage)

解决方案:确保首次渲染的输出在服务器和客户端是一致的。对于需要在客户端动态变化的内容,使用 useEffect 在 hydration 完成后再更新。

import { useEffect, useState } from 'react';

function MyComponent() {
  // 首次渲染使用默认值,保证服务器和客户端一致
  const [theme, setTheme] = useState('light');

  useEffect(() => {
    // hydration 完成后再从 localStorage 读取
    const savedTheme = localStorage.getItem('theme');
    if (savedTheme) {
      setTheme(savedTheme);
    }
  }, []);

  return <div className={theme}>...</div>;
}

刷新页面时出现 404

现象:在站点内通过链接导航到其他页面正常,但刷新页面后出现 404 错误。

原因:当你刷新页面或直接访问某个 URL 时,请求会发送到服务器,但服务器上可能并没有对应路径的文件(尤其是在某些静态托管服务中)。

解决方案:大部分静态托管服务(如 GitHub Pages、Netlify、Vercel 等)默认会使用 404.html 来处理所有未匹配的路由,无需额外配置。如果你的服务器没有自动处理,需要手动配置将未匹配的请求重定向到 404.html。Rspress 生成的 404.html 包含完整的应用代码,能够在客户端正确处理路由并显示对应页面。

如果你的托管服务支持 _redirects 文件,可以使用以下配置示例:

docs/public/_redirects
/*    /404.html   200

配置选项

你可以通过 ssg 配置来控制是否启用 SSG:

rspress.config.ts
import { defineConfig } from '@rspress/core';

export default defineConfig({
  ssg: true, // 默认值,启用 SSG
});

如果你的站点有特殊需求,可以禁用 SSG:

rspress.config.ts
import { defineConfig } from '@rspress/core';

export default defineConfig({
  ssg: false, // 禁用 SSG,使用 CSR
});
注意

请谨慎关闭 SSG,这会丧失首屏渲染速度和 SEO 优势。

自定义 HTML 内容

通过 builderConfig.html.tags,你可以向 HTML 中注入自定义内容,比如添加统计代码、脚本或样式:

rspress.config.ts
import { defineConfig } from '@rspress/core';

export default defineConfig({
  builderConfig: {
    html: {
      tags: [
        {
          tag: 'script',
          attrs: {
            src: 'https://cdn.example.com/analytics.js',
          },
        },
      ],
    },
  },
});

更多配置详情请参考 Rsbuild html.tags 文档