多版本文档管理
Rspress 提供强大的版本管理功能,允许您为软件的不同版本维护多份文档。本章探讨如何设置和管理多版本文档站点。
什么是文档版本管理?
版本管理概述
文档版本管理允许您为产品的每个主要版本维护独立的文档:
- 多个版本: 同时维护 v1.0, v2.0, v3.0 文档
- 版本切换: 用户可以在版本之间切换
- 版本归档: 保留旧版本供参考
- 独立内容: 每个版本有自己的内容和配置
为什么需要版本管理?
用户场景:
- 用户可能使用不同版本的软件
- 升级需要时间,用户需要访问旧文档
- API 更改需要不同的文档
- 功能在版本之间变化
维护优势:
- 清晰的版本历史
- 更容易跟踪更改
- 更好的用户体验
- 避免混淆
基本版本设置
目录结构
组织多版本文档:
docs/
├── v1/ # 版本 1.x 文档
│ ├── index.md
│ ├── guide/
│ │ ├── getting-started.md
│ │ └── api.md
│ └── examples/
├── v2/ # 版本 2.x 文档
│ ├── index.md
│ ├── guide/
│ │ ├── getting-started.md
│ │ ├── new-features.md
│ │ └── api.md
│ └── examples/
├── v3/ # 版本 3.x (最新)
│ ├── index.md
│ ├── guide/
│ │ ├── getting-started.md
│ │ ├── advanced.md
│ │ └── api.md
│ └── examples/
└── versions.json # 版本配置版本配置
创建 versions.json:
json
{
"latest": "v3",
"versions": [
{
"version": "v3",
"label": "v3.x (Latest)",
"path": "/v3"
},
{
"version": "v2",
"label": "v2.x",
"path": "/v2"
},
{
"version": "v1",
"label": "v1.x",
"path": "/v1"
}
]
}Rspress 配置
在 rspress.config.ts 中配置版本:
typescript
import { defineConfig } from 'rspress/config';
import versions from './versions.json';
export default defineConfig({
title: 'My Documentation',
// 版本配置
themeConfig: {
// 添加版本选择器
versioning: {
latestVersion: versions.latest,
versions: versions.versions,
},
// 为每个版本配置导航
nav: [
{ text: 'Home', link: '/' },
{ text: 'Guide', link: `/${versions.latest}/guide/` },
{ text: 'API', link: `/${versions.latest}/api/` },
{
text: `Version: ${versions.latest}`,
items: versions.versions.map(v => ({
text: v.label,
link: v.path,
})),
},
],
},
});版本选择器
内置版本选择器
Rspress 提供内置的版本选择器组件:
typescript
export default defineConfig({
themeConfig: {
// 启用版本选择器
versioning: {
enabled: true,
latestVersion: 'v3',
versions: [
{ version: 'v3', label: 'v3.x (Latest)' },
{ version: 'v2', label: 'v2.x' },
{ version: 'v1', label: 'v1.x (LTS)' },
],
// 版本选择器位置
position: 'navbar', // 或 'sidebar'
},
},
});自定义版本选择器
创建自定义版本切换组件:
tsx
// components/VersionSelector.tsx
import React from 'react';
import { useLocation } from 'rspress/runtime';
import versions from '../versions.json';
export function VersionSelector() {
const location = useLocation();
const currentVersion = location.pathname.split('/')[1] || versions.latest;
const handleVersionChange = (version: string) => {
const currentPath = location.pathname.replace(`/${currentVersion}`, `/${version}`);
window.location.href = currentPath;
};
return (
<div className="version-selector">
<label htmlFor="version-select">Version:</label>
<select
id="version-select"
value={currentVersion}
onChange={(e) => handleVersionChange(e.target.value)}
className="version-dropdown"
>
{versions.versions.map((v) => (
<option key={v.version} value={v.version}>
{v.label}
</option>
))}
</select>
</div>
);
}样式化版本选择器:
css
/* styles/version-selector.css */
.version-selector {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
}
.version-selector label {
font-size: 0.875rem;
color: var(--rp-c-text-2);
}
.version-dropdown {
padding: 0.375rem 0.75rem;
border: 1px solid var(--rp-c-border);
border-radius: 6px;
background: var(--rp-c-bg);
color: var(--rp-c-text-1);
font-size: 0.875rem;
cursor: pointer;
transition: all 0.3s;
}
.version-dropdown:hover {
border-color: var(--rp-c-brand);
}
.version-dropdown:focus {
outline: none;
border-color: var(--rp-c-brand);
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}版本横幅
过时版本警告
为旧版本添加警告横幅:
tsx
// components/VersionBanner.tsx
import React from 'react';
import { useLocation } from 'rspress/runtime';
import versions from '../versions.json';
export function VersionBanner() {
const location = useLocation();
const currentVersion = location.pathname.split('/')[1];
const isLatest = currentVersion === versions.latest;
if (isLatest) {
return null;
}
const latestVersionInfo = versions.versions.find(
v => v.version === versions.latest
);
return (
<div className="version-banner warning">
<div className="banner-content">
<span className="banner-icon">⚠️</span>
<span className="banner-text">
您正在查看 <strong>{currentVersion}</strong> 的文档。
最新版本是{' '}
<a href={latestVersionInfo?.path}>
{latestVersionInfo?.label}
</a>
。
</span>
</div>
</div>
);
}样式化横幅:
css
/* styles/version-banner.css */
.version-banner {
padding: 1rem;
margin-bottom: 2rem;
border-radius: 8px;
background: #fff3cd;
border: 1px solid #ffc107;
color: #856404;
}
.version-banner.warning {
background: #fff3cd;
border-color: #ffc107;
color: #856404;
}
.version-banner.info {
background: #d1ecf1;
border-color: #17a2b8;
color: #0c5460;
}
.banner-content {
display: flex;
align-items: center;
gap: 0.75rem;
}
.banner-icon {
font-size: 1.5rem;
flex-shrink: 0;
}
.banner-text {
line-height: 1.6;
}
.banner-text a {
color: inherit;
text-decoration: underline;
font-weight: 600;
}
.banner-text a:hover {
opacity: 0.8;
}版本导航
每个版本的侧边栏
为不同版本配置不同的侧边栏:
typescript
export default defineConfig({
themeConfig: {
sidebar: {
// v3 侧边栏
'/v3/': [
{
text: '入门',
items: [
{ text: '介绍', link: '/v3/guide/introduction' },
{ text: '快速开始', link: '/v3/guide/getting-started' },
{ text: '新功能', link: '/v3/guide/new-features' },
],
},
{
text: 'API',
items: [
{ text: 'API 参考', link: '/v3/api/reference' },
{ text: '高级用法', link: '/v3/api/advanced' },
],
},
],
// v2 侧边栏
'/v2/': [
{
text: '入门',
items: [
{ text: '介绍', link: '/v2/guide/introduction' },
{ text: '快速开始', link: '/v2/guide/getting-started' },
],
},
{
text: 'API',
items: [
{ text: 'API 参考', link: '/v2/api/reference' },
],
},
],
// v1 侧边栏
'/v1/': [
{
text: '文档',
items: [
{ text: '介绍', link: '/v1/guide/introduction' },
{ text: 'API', link: '/v1/api/reference' },
],
},
],
},
},
});版本特定链接
在每个版本中维护正确的链接:
markdown
<!-- v3/guide/introduction.md -->
---
title: 介绍
---
# 介绍 v3
欢迎使用版本 3!
## 下一步
- [快速开始](./getting-started.md)
- [新功能](./new-features.md)
- [API 参考](/v3/api/reference)
## 从 v2 迁移
查看我们的[迁移指南](./migration-from-v2.md)。版本管理工作流
创建新版本
发布新主要版本时:
- 复制当前文档
bash
# 复制 v3 到 v4
cp -r docs/v3 docs/v4
# 将 v3 标记为旧版本
# 更新 v4 内容为新功能- 更新版本配置
json
{
"latest": "v4",
"versions": [
{
"version": "v4",
"label": "v4.x (Latest)",
"path": "/v4"
},
{
"version": "v3",
"label": "v3.x",
"path": "/v3"
},
{
"version": "v2",
"label": "v2.x (Archived)",
"path": "/v2"
}
]
}- 更新配置文件
typescript
export default defineConfig({
themeConfig: {
versioning: {
latestVersion: 'v4',
// ...更新版本列表
},
},
});维护多个版本
最佳实践:
- 主要更新: 仅更新最新版本
- 关键修复: 回溯到支持的版本
- 安全更新: 更新所有活跃版本
- 归档: 将非常旧的版本标记为已归档
bash
# 示例工作流
git checkout main # 最新版本 (v4)
# 进行更改...
git checkout v3-docs # 支持版本
# 回溯重要修复...
git checkout v2-archived # 仅关键安全修复长期支持 (LTS)
标记 LTS 版本
指示哪些版本是 LTS:
json
{
"latest": "v4",
"versions": [
{
"version": "v4",
"label": "v4.x (Latest)",
"path": "/v4",
"lts": false
},
{
"version": "v3",
"label": "v3.x (LTS)",
"path": "/v3",
"lts": true,
"endOfLife": "2025-12-31"
},
{
"version": "v2",
"label": "v2.x (EOL)",
"path": "/v2",
"lts": false,
"endOfLife": "2024-06-30"
}
]
}LTS 横幅
显示 LTS 信息:
tsx
export function LTSBanner({ version }) {
const versionInfo = versions.versions.find(v => v.version === version);
if (!versionInfo) return null;
if (versionInfo.lts) {
return (
<div className="version-banner info">
<span className="banner-icon">✅</span>
<span className="banner-text">
此版本是 LTS(长期支持)。支持到{' '}
{new Date(versionInfo.endOfLife).toLocaleDateString('zh-CN')}。
</span>
</div>
);
}
if (versionInfo.endOfLife && new Date(versionInfo.endOfLife) < new Date()) {
return (
<div className="version-banner warning">
<span className="banner-icon">⚠️</span>
<span className="banner-text">
此版本已达到生命周期终点 (EOL),不再接收更新。
</span>
</div>
);
}
return null;
}版本特定功能
功能标志
根据版本显示内容:
mdx
import { VersionFeature } from '../components/VersionFeature';
# API 参考
<VersionFeature minVersion="v3">
## 新的异步 API
v3 引入了新的基于 Promise 的 API:
````javascript
// v3+ 中可用
const result = await api.fetchData();
```
</VersionFeature>
<VersionFeature maxVersion="v2">
## 传统回调 API
v2 及更早版本使用回调:
```javascript
// v2 及更早版本
api.fetchData((error, result) => {
if (error) {
console.error(error);
}
});
```
</VersionFeature>
```
实现 VersionFeature 组件:
```tsx
// components/VersionFeature.tsx
import React from 'react';
import { useLocation } from 'rspress/runtime';
interface VersionFeatureProps {
minVersion?: string;
maxVersion?: string;
children: React.ReactNode;
}
export function VersionFeature({
minVersion,
maxVersion,
children,
}: VersionFeatureProps) {
const location = useLocation();
const currentVersion = location.pathname.split('/')[1];
// 简单版本比较
const versionNumber = (v: string) => parseInt(v.replace('v', ''), 10);
const current = versionNumber(currentVersion);
if (minVersion && current < versionNumber(minVersion)) {
return null;
}
if (maxVersion && current > versionNumber(maxVersion)) {
return null;
}
return <>{children}</>;
}
```
## 版本重定向
### 自动重定向
将根路径重定向到最新版本:
```typescript
export default defineConfig({
builderConfig: {
html: {
redirects: [
{
from: '/',
to: '/v4/',
},
{
from: '/guide',
to: '/v4/guide/',
},
],
},
},
});
```
### 智能版本检测
根据用户上下文重定向:
```tsx
// components/VersionRedirect.tsx
import { useEffect } from 'react';
import { useLocation } from 'rspress/runtime';
export function VersionRedirect() {
const location = useLocation();
useEffect(() => {
// 检查是否访问已归档版本
const currentVersion = location.pathname.split('/')[1];
const savedVersion = localStorage.getItem('preferredVersion');
// 如果用户有首选版本,重定向
if (savedVersion && savedVersion !== currentVersion) {
const newPath = location.pathname.replace(
`/${currentVersion}`,
`/${savedVersion}`
);
window.location.href = newPath;
}
}, [location]);
return null;
}
```
## 版本搜索
### 每个版本的搜索
配置版本特定的搜索:
```typescript
export default defineConfig({
themeConfig: {
search: {
versioned: true,
// 为每个版本创建单独的搜索索引
indexes: [
{ version: 'v4', path: '/v4' },
{ version: 'v3', path: '/v3' },
{ version: 'v2', path: '/v2' },
],
},
},
});
```
## 最佳实践
### 版本命名
1. **语义化版本**: 使用 v1.x, v2.x, v3.x
2. **清晰标签**: "v3.x (Latest)", "v2.x (LTS)", "v1.x (Archived)"
3. **一致性**: 在所有文档中使用相同的命名
### 内容管理
1. **保持同步**: 定期更新所有支持的版本
2. **清晰迁移指南**: 提供版本间迁移路径
3. **突出显示更改**: 标记每个版本中的新功能
4. **归档旧版本**: 清楚标记 EOL 版本
### 用户体验
1. **默认最新版本**: 新用户应看到最新文档
2. **保留用户选择**: 记住用户选择的版本
3. **清晰的视觉提示**: 显示当前版本
4. **简单切换**: 版本间切换应该容易
## 自动化版本管理
### 版本发布脚本
```bash
#!/bin/bash
# scripts/release-version.sh
NEW_VERSION=$1
CURRENT_VERSION=$(jq -r '.latest' versions.json)
if [ -z "$NEW_VERSION" ]; then
echo "用法: ./release-version.sh v4"
exit 1
fi
echo "创建新版本: $NEW_VERSION"
# 复制当前版本
cp -r "docs/$CURRENT_VERSION" "docs/$NEW_VERSION"
# 更新 versions.json
jq ".latest = \"$NEW_VERSION\"" versions.json > versions.tmp.json
jq ".versions |= [{
version: \"$NEW_VERSION\",
label: \"$NEW_VERSION.x (Latest)\",
path: \"/$NEW_VERSION\"
}] + ." versions.tmp.json > versions.json
rm versions.tmp.json
echo "✅ 版本 $NEW_VERSION 已创建"
echo "现在更新 docs/$NEW_VERSION 中的内容"
```
### 版本归档脚本
```bash
#!/bin/bash
# scripts/archive-version.sh
VERSION=$1
if [ -z "$VERSION" ]; then
echo "用法: ./archive-version.sh v2"
exit 1
fi
echo "归档版本: $VERSION"
# 更新版本标签
jq "(.versions[] | select(.version == \"$VERSION\") | .label) |= . + \" (Archived)\"" \
versions.json > versions.tmp.json
mv versions.tmp.json versions.json
echo "✅ 版本 $VERSION 已归档"
```
## 故障排除
### 版本链接损坏
**问题**: 链接指向错误的版本
**解决方案**:
```markdown
<!-- ✅ 使用绝对路径 -->
[API 参考](/v3/api/reference)
<!-- ❌ 避免相对路径跨版本 -->
[API 参考](../../v2/api/reference)
```
### 版本选择器未显示
**问题**: 版本选择器不出现
**检查**:
1. `versions.json` 格式正确
2. 配置中启用版本控制
3. 至少配置 2 个版本
### 搜索显示所有版本
**问题**: 搜索结果混合所有版本
**解决方案**:
```typescript
export default defineConfig({
themeConfig: {
search: {
versioned: true, // 启用版本化搜索
},
},
});
```
---
## 下一步
- 探索[内置组件](./built-in-components.md)
- 学习[构建能力扩展](./build-extensions.md)
- 了解[自定义搜索](./custom-search.md)
---
::: tip 最佳实践
- 始终维护至少一个 LTS 版本
- 为每个版本提供清晰的迁移指南
- 自动化版本创建过程
- 保持版本 URL 结构一致
- 为旧版本添加清晰的警告横幅
:::
::: warning 常见陷阱
- 不要忘记更新内部链接
- 不要在版本间混合内容
- 不要无限期维护所有版本
- 不要使用模糊的版本标签
- 不要忘记更新搜索索引
:::
::: info 资源
- [语义化版本](https://semver.org/lang/zh-CN/)
- [文档版本管理最佳实践](https://www.writethedocs.org/)
- [Rspress 配置参考](https://rspress.dev/api/config)
:::