Skip to content

静态网站生成与服务端渲染

理解 Rspress 如何构建和渲染您的文档对于优化和部署至关重要。本章探讨静态网站生成(SSG)、服务端渲染(SSR)以及 Rspress 中的构建过程。

理解 SSG vs SSR

静态网站生成 (SSG)

SSG 在构建时预渲染所有页面,生成静态 HTML 文件:

构建时:
Markdown/MDX → HTML + CSS + JS → 静态文件

运行时:
用户请求 → 服务器 → 静态 HTML (快速!)

优势:

  • 极快的页面加载
  • 更好的 SEO(内容立即可用)
  • 更低的服务器成本(只需提供静态文件)
  • 可以在简单托管上工作(不需要服务器)
  • 可以在 CDN 上缓存

最适合:

  • 文档网站
  • 博客
  • 营销页面
  • 不经常变化的内容

服务端渲染 (SSR)

SSR 在每次请求时渲染页面:

运行时:
用户请求 → 服务器渲染 HTML → 响应

每个请求生成新的 HTML

优势:

  • 始终最新的内容
  • 可以为用户个性化
  • 访问请求数据
  • 动态内容生成

最适合:

  • 用户仪表板
  • 个性化内容
  • 频繁变化的数据
  • 动态应用程序

Rspress 的方法

Rspress 主要使用 SSG,并具有可选的 SSR 能力:

typescript
// 默认: 纯 SSG
npm run build  // 生成静态 HTML 文件

// 使用 SSR: 混合方法(高级)
// Rspress 也可以进行客户端水合

构建过程

构建管道

理解 Rspress 的构建管道:

mermaid
graph LR
    A[源文件] --> B[解析 MDX/MD]
    B --> C[处理组件]
    C --> D[生成路由]
    D --> E[打包资源]
    E --> F[生成 HTML]
    F --> G[优化输出]
    G --> H[静态文件]

构建命令

bash
# 开发构建(快速,无优化)
npm run dev

# 生产构建(优化)
npm run build

# 预览生产构建
npm run preview

构建输出

构建后,Rspress 生成:

doc_build/
├── index.html                    # 首页
├── guide/
│   ├── index.html               # /guide/
│   ├── installation.html        # /guide/installation
│   └── configuration.html       # /guide/configuration
├── assets/
│   ├── chunks/                  # 代码分割的 JS 块
│   ├── css/                     # 编译的 CSS
│   └── images/                  # 优化的图片
└── _rspress/
    └── metadata.json            # 构建元数据

构建配置

基本构建设置

配置构建过程:

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

export default defineConfig({
  root: 'docs',
  outDir: 'doc_build',  // 输出目录

  // 构建配置
  builderConfig: {
    // 输出格式
    output: {
      cleanDistPath: true,  // 构建前清理输出
      distPath: {
        root: 'doc_build',
      },
    },

    // 性能
    performance: {
      chunkSplit: {
        strategy: 'split-by-experience',
      },
    },
  },
});

优化设置

优化构建输出:

typescript
export default defineConfig({
  builderConfig: {
    // 压缩
    output: {
      minify: {
        js: true,
        css: true,
        html: true,
      },
    },

    // 代码分割
    performance: {
      chunkSplit: {
        strategy: 'split-by-experience',
        forceSplitting: [
          /node_modules/,
        ],
      },
    },

    // Source maps (用于调试)
    output: {
      sourceMap: {
        js: 'source-map',  // 或在生产中为 false
        css: true,
      },
    },
  },
});

预渲染

预渲染如何工作

Rspress 使用 React SSR 预渲染页面:

  1. 解析内容: 读取和解析 Markdown/MDX
  2. 执行 React: 运行 React 组件生成 HTML
  3. 序列化状态: 捕获应用程序状态
  4. 生成 HTML: 创建带内联状态的完整 HTML
  5. 优化: 压缩和优化输出

预渲染配置

控制预渲染行为:

typescript
export default defineConfig({
  // 预渲染所有路由
  ssg: {
    strict: true,  // 在错误时构建失败
  },

  // 路由特定配置
  route: {
    // 要预渲染的路由
    include: ['/', '/guide/**', '/api/**'],

    // 要跳过的路由
    exclude: ['**/draft/**'],
  },
});

水合

什么是水合?

水合使静态 HTML 具有交互性:

1. 服务器发送静态 HTML (快速初始加载)
2. 浏览器下载 JavaScript
3. React "水合" HTML (附加事件监听器)
4. 页面变得完全交互

Rspress 中的水合

tsx
// 在构建期间 (SSG)
<button>点击我</button>

// 水合后 (交互式)
<button onClick={handleClick}>点击我</button>

优化水合

typescript
// 懒加载重型组件
import { lazy, Suspense } from 'react';

const HeavyComponent = lazy(() => import('./HeavyComponent'));

export function Page() {
  return (
    <Suspense fallback={<div>加载中...</div>}>
      <HeavyComponent />
    </Suspense>
  );
}

代码分割

自动代码分割

Rspress 按路由自动分割代码:

首页:     homepage.js (50KB)
指南页面: guide.js (30KB)
API 页面: api.js (40KB)
共享:     vendor.js (100KB)

手动代码分割

分割大型组件:

tsx
// 而不是这样(立即加载所有内容)
import { Chart } from './Chart';
import { Table } from './Table';
import { Graph } from './Graph';

// 这样做(按需加载)
const Chart = lazy(() => import('./Chart'));
const Table = lazy(() => import('./Table'));
const Graph = lazy(() => import('./Graph'));

构建性能

测量构建时间

bash
# 测量构建时间
time npm run build

# 启用详细日志
npm run build -- --verbose

# 分析 bundle 大小
npm run build -- --analyze

优化策略

  1. 减少依赖
typescript
// 重型库 (100KB)
import _ from 'lodash';

// 轻量替代 (10KB)
import debounce from 'lodash/debounce';
  1. 懒加载重型内容
mdx
import { lazy } from 'react';

const VideoPlayer = lazy(() => import('./VideoPlayer'));

<Suspense fallback="加载中...">
  <VideoPlayer />
</Suspense>
  1. 优化图片
bash
# 在添加到文档之前
imagemin docs/images/* --out-dir=docs/images/optimized

缓存

启用构建缓存以加快重建:

typescript
export default defineConfig({
  builderConfig: {
    // 启用缓存
    cache: true,
  },
});

构建模式

开发模式

快速构建和热重载:

bash
npm run dev

# 特性:
- 快速增量构建
- 热模块替换
- 启用 source maps
- 无压缩
- 详细错误消息

生产模式

优化的部署构建:

bash
npm run build

# 特性:
- 完全优化
- 启用压缩
- Tree shaking
- 死代码消除
- 资源优化

预览模式

本地测试生产构建:

bash
# 先构建
npm run build

# 然后预览
npm run preview

# 在 http://localhost:4173 访问

高级构建功能

自定义构建脚本

添加自定义构建脚本:

json
// package.json
{
  "scripts": {
    "build": "rspress build",
    "build:analyze": "rspress build --analyze",
    "build:staging": "NODE_ENV=staging rspress build",
    "build:production": "NODE_ENV=production rspress build"
  }
}

环境变量

在构建中使用环境变量:

typescript
// rspress.config.ts
export default defineConfig({
  define: {
    'process.env.API_URL': JSON.stringify(
      process.env.NODE_ENV === 'production'
        ? 'https://api.production.com'
        : 'https://api.staging.com'
    ),
  },
});

SEO 优化

Meta 标签

在构建期间添加 meta 标签:

typescript
export default defineConfig({
  head: [
    ['meta', { name: 'description', content: '网站描述' }],
    ['meta', { name: 'keywords', content: '关键词, 这里' }],
    ['meta', { property: 'og:title', content: '网站标题' }],
    ['meta', { property: 'og:description', content: '描述' }],
  ],
});

故障排除

构建失败

  1. 内存问题
bash
# 增加 Node 内存
NODE_OPTIONS=--max_old_space_size=4096 npm run build
  1. 缺少依赖
bash
# 清洁安装
rm -rf node_modules package-lock.json
npm install

慢速构建

  1. 在生产中禁用 source maps
  2. 减少依赖
  3. 使用构建缓存
  4. 在构建前优化图片
  5. 将大页面分割成较小的页面

最佳实践

构建检查清单

在部署前:

  • [ ] 运行生产构建
  • [ ] 使用预览测试
  • [ ] 检查 bundle 大小
  • [ ] 验证所有页面加载
  • [ ] 在移动设备上测试
  • [ ] 检查 Lighthouse 分数
  • [ ] 验证 HTML
  • [ ] 测试所有链接

下一步


性能提示

  • 启用构建缓存以加快重建
  • 对大型站点使用代码分割
  • 在添加图片之前优化它们
  • 定期监控 bundle 大小

生产构建

  • 在部署前始终测试生产构建
  • 在生产中启用压缩
  • 在生产中禁用 source maps
  • 使用环境变量进行配置