Monorepo 文档管理
在 Monorepo 架构中,多个包可能需要共享文档或各自维护文档。本文介绍如何在 Monorepo 中高效管理 VitePress 文档。
常见架构模式
模式一:集中式文档
text
monorepo/
├── packages/
│ ├── core/
│ ├── utils/
│ └── ui/
└── docs/ # 集中式文档站
├── .vitepress/
├── guide/
├── packages/ # 各包的文档
│ ├── core.md
│ ├── utils.md
│ └── ui.md
└── index.md适用场景:包之间关联紧密,文档需要交叉引用。
模式二:分布式文档
text
monorepo/
├── packages/
│ ├── core/
│ │ ├── src/
│ │ └── docs/ # core 的独立文档
│ │ ├── .vitepress/
│ │ └── index.md
│ ├── utils/
│ │ └── docs/ # utils 的独立文档
│ │ ├── .vitepress/
│ │ └── index.md
│ └── ui/
│ └── docs/ # ui 的独立文档
│ ├── .vitepress/
│ └── index.md
└── docs/ # 根文档站(汇总入口)
├── .vitepress/
└── index.md适用场景:各包相对独立,需要独立部署文档。
模式三:混合模式
text
monorepo/
├── packages/
│ ├── core/
│ │ └── docs/ # 核心包有独立文档
│ └── ui/
└── docs/ # 主文档站(包含共享内容和子包引用)
├── .vitepress/
├── packages/
│ ├── core.md # 可从 core/docs 同步
│ └── ui.md
└── index.md适用场景:部分包需要独立文档,部分包共享主文档站。
pnpm Workspace 方案
项目结构
text
monorepo/
├── pnpm-workspace.yaml
├── package.json
├── packages/
│ ├── core/
│ └── ui/
└── docs/
├── package.json
└── .vitepress/workspace 配置
yaml
# pnpm-workspace.yaml
packages:
- 'packages/*'
- 'docs'docs 的 package.json
json
{
"name": "@myrepo/docs",
"private": true,
"scripts": {
"dev": "vitepress dev",
"build": "vitepress build",
"preview": "vitepress preview"
},
"dependencies": {
"@myrepo/core": "workspace:*",
"@myrepo/ui": "workspace:*"
},
"devDependencies": {
"vitepress": "^2.0.0"
}
}workspace 协议
使用 workspace:* 协议直接引用 Monorepo 内的其他包,无需发布到 npm。
在文档中引用包内容
typescript
// docs/.vitepress/config.mts
import { version } from '@myrepo/core/package.json'
export default defineConfig({
themeConfig: {
nav: [
{ text: `Core v${version}`, link: '/packages/core' }
]
}
})Turborepo 方案
项目结构
text
monorepo/
├── turbo.json
├── package.json
├── apps/
│ └── docs/ # 文档应用
│ ├── package.json
│ └── .vitepress/
└── packages/
├── core/
└── ui/turbo.json 配置
json
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".vitepress/dist/**"]
},
"dev": {
"dependsOn": ["^build"],
"persistent": true,
"cache": false
}
}
}文档构建任务
json
// apps/docs/package.json
{
"name": "@myrepo/docs",
"scripts": {
"dev": "vitepress dev",
"build": "vitepress build",
"preview": "vitepress preview"
}
}执行构建
bash
# 构建所有包和文档
turbo build
# 仅开发文档
turbo dev --filter=@myrepo/docs
# 构建文档及其依赖
turbo build --filter=@myrepo/docs...自动生成 API 文档
从源码提取 API 文档
typescript
// scripts/gen-api-docs.mts
import fs from 'fs'
import path from 'path'
interface ApiEntry {
name: string
type: 'function' | 'interface' | 'type' | 'class'
description: string
params?: { name: string; type: string; description: string }[]
returns?: string
example?: string
}
function generateApiMarkdown(entries: ApiEntry[], packageName: string): string {
let md = `---\ntitle: ${packageName} API\ndescription: ${packageName} 包的 API 参考\ntags:\n - API\n - ${packageName}\n---\n\n`
md += `# ${packageName} API\n\n`
for (const entry of entries) {
md += `## ${entry.name}\n\n`
md += `${entry.description}\n\n`
if (entry.type === 'function' && entry.params) {
md += `### 参数\n\n`
md += `| 参数 | 类型 | 说明 |\n`
md += `|------|------|------|\n`
for (const param of entry.params) {
md += `| \`${param.name}\` | \`${param.type}\` | ${param.description} |\n`
}
md += '\n'
}
if (entry.returns) {
md += `### 返回值\n\n\`${entry.returns}\`\n\n`
}
if (entry.example) {
md += `### 示例\n\n\`\`\`typescript\n${entry.example}\n\`\`\`\n\n`
}
}
return md
}
// 示例用法
const entries: ApiEntry[] = [
{
name: 'createApp',
type: 'function',
description: '创建应用实例',
params: [
{ name: 'config', type: 'AppConfig', description: '应用配置' }
],
returns: 'Application',
example: "const app = createApp({ title: '我的文档' })"
}
]
const md = generateApiMarkdown(entries, '@myrepo/core')
fs.writeFileSync('docs/packages/core-api.md', md, 'utf-8')使用 TypeDoc 生成文档
bash
npm install -D typedoc typedoc-plugin-markdownjson
// typedoc.json
{
"entryPoints": ["packages/core/src/index.ts"],
"out": "docs/packages/core-api",
"plugin": ["typedoc-plugin-markdown"],
"readme": "none",
"excludePrivate": true,
"excludeProtected": true
}共享主题和组件
共享 VitePress 主题
text
monorepo/
├── packages/
│ └── docs-theme/ # 共享主题包
│ ├── package.json
│ ├── index.ts
│ ├── components/
│ └── styles/
└── apps/
└── docs/
└── .vitepress/
└── theme/
└── index.ts # 引用共享主题typescript
// packages/docs-theme/index.ts
import DefaultTheme from 'vitepress/theme'
import './styles/index.css'
// 导出共享组件
export { default as PackageHeader } from './components/PackageHeader.vue'
export { default as ApiEndpoint } from './components/ApiEndpoint.vue'
export { default as InstallGuide } from './components/InstallGuide.vue'
export default {
...DefaultTheme,
enhanceApp({ app }) {
// 注册共享组件
app.component('PackageHeader', () => import('./components/PackageHeader.vue'))
app.component('ApiEndpoint', () => import('./components/ApiEndpoint.vue'))
app.component('InstallGuide', () => import('./components/InstallGuide.vue'))
}
}typescript
// apps/docs/.vitepress/theme/index.ts
import Theme from '@myrepo/docs-theme'
export default Theme版本化文档管理
多版本侧边栏
typescript
// .vitepress/config.mts
function getVersionedSidebar(version: string) {
return [
{
text: `${version} 指南`,
items: [
{ text: '快速开始', link: `/${version}/guide/getting-started` },
{ text: 'API 参考', link: `/${version}/api/` }
]
}
]
}
export default defineConfig({
themeConfig: {
nav: [
{
text: '版本',
items: [
{ text: 'v3 (最新)', link: '/v3/guide/' },
{ text: 'v2', link: '/v2/guide/' },
{ text: 'v1 (已归档)', link: '/v1/guide/' }
]
}
]
}
})版本切换组件
vue
<!-- packages/docs-theme/components/VersionSwitcher.vue -->
<script setup lang="ts">
import { useData } from 'vitepress'
const { site } = useData()
const versions = [
{ label: 'v3 (最新)', link: '/v3/' },
{ label: 'v2', link: '/v2/' },
{ label: 'v1', link: '/v1/' }
]
</script>
<template>
<div class="version-switcher">
<select @change="($event) => $window.location.href = ($event.target as HTMLSelectElement).value">
<option
v-for="v in versions"
:key="v.link"
:value="v.link"
>
{{ v.label }}
</option>
</select>
</div>
</template>CI/CD 配置
GitHub Actions
yaml
# .github/workflows/docs.yml
name: Deploy Docs
on:
push:
branches: [main]
paths:
- 'docs/**'
- 'packages/*/src/**'
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- name: Install
run: pnpm install
- name: Build packages
run: pnpm -r --filter='./packages/*' build
- name: Build docs
run: pnpm --filter @myrepo/docs build
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: apps/docs/.vitepress/dist最佳实践
| 实践 | 说明 |
|---|---|
| 文档就近放置 | 各包的文档放在包目录下,便于维护 |
| 共享主题 | 提取公共主题和组件到共享包 |
| 自动生成 API | 使用 TypeDoc 等工具从源码生成 API 文档 |
| 版本化管理 | 为不同版本维护独立文档 |
| 按需构建 | 使用 Turborepo 的 filter 功能只构建变更的文档 |
| 链接验证 | CI 中添加文档链接检查步骤 |
相关链接
- Turborepo 官方文档 — Monorepo 构建工具
- pnpm Workspace — pnpm 工作区配置
- 多版本文档管理 — 版本化文档方案
- 构建优化 — 构建性能优化
- 部署与发布 — 文档部署方案