VitePress 文档站点:打造专业级组件文档(含交互式示例)
本文是《从零到一:构建现代化企业级 Monorepo 项目实战》系列的第六篇。这篇文章将教你如何搭建一个媲美官方文档的专业文档站点。
🎯 本文目标
- VitePress 在 Monorepo 中的集成
- 组件示例自动导入和渲染
- 自定义主题和样式
- 文档站点性能优化
- 部署配置
📖 为什么选择 VitePress?
文档工具对比
| 工具 | 性能 | Vue 支持 | Markdown 扩展 | 学习成本 | 推荐指数 | 
|---|---|---|---|---|---|
| VuePress | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | 
| Docusaurus | ⭐⭐⭐ | ❌ React | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | 
| Docsify | ⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐ | 
| VitePress | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 
VitePress 优势:
- ⚡ 基于 Vite,开发和构建速度极快
- 🎨 Vue 组件可以直接在 Markdown 中使用
- 📝 Markdown 扩展能力强大
- 🎯 专为 Vue 生态设计
🏗️ 项目结构设计
docs/
├── .vitepress/
│   ├── config.ts              # 配置文件
│   ├── theme/
│   │   ├── index.ts           # 主题入口
│   │   ├── components/
│   │   │   └── DemoBlock.vue  # 示例容器组件
│   │   └── custom.css         # 自定义样式
│   └── plugins/
│       └── demo-container.ts  # 自定义插件
├── components/
│   ├── button/
│   │   ├── index.md           # Button 文档
│   │   └── basic.vue          # 基础示例
│   └── index.md
├── guide/
│   ├── index.md
│   └── getting-started.md
├── index.md                   # 首页
└── package.json
🚀 第一步:安装和配置
1.1 安装 VitePress
cd docs
pnpm init
pnpm add -D vitepress vue
1.2 基础配置
// docs/.vitepress/config.ts
import { defineConfig } from 'vitepress'
import { resolve } from 'path'
export default defineConfig({
  title: 'GDU Common',
  description: 'GDU 前端通用组件库和工具集',
  lang: 'zh-CN',
  // 主题配置
  themeConfig: {
    logo: '/logo.svg',
    nav: [
      { text: '指南', link: '/guide/' },
      { text: '组件', link: '/components/' },
      { text: '工具', link: '/utils/' },
    ],
    sidebar: {
      '/guide/': [
        {
          text: '开始',
          items: [
            { text: '介绍', link: '/guide/' },
            { text: '快速开始', link: '/guide/getting-started' },
          ],
        },
      ],
      '/components/': [
        {
          text: '组件',
          items: [{ text: 'Button 按钮', link: '/components/button' }],
        },
      ],
    },
    socialLinks: [{ icon: 'github', link: 'https://github.com/your-org/gdu-common' }],
    search: {
      provider: 'local', // 本地搜索
    },
  },
  // Vite 配置
  vite: {
    resolve: {
      alias: {
        '@gdu-common/ui': resolve(__dirname, '../../packages/ui/src'),
        '@gdu-common/utils': resolve(__dirname, '../../packages/utils/src'),
        '@gdu-common/shared': resolve(__dirname, '../../packages/shared/src'),
      },
    },
  },
})
1.3 添加脚本
// docs/package.json
{
  "scripts": {
    "dev": "vitepress dev",
    "build": "vitepress build",
    "preview": "vitepress preview"
  }
}
🎨 第二步:自定义主题
2.1 创建自定义样式
/* docs/.vitepress/theme/custom.css */
/* 首页渐变背景 */
:root {
  --vp-home-hero-name-color: transparent;
  --vp-home-hero-name-background: linear-gradient(120deg, #bd34fe 30%, #41d1ff);
}
/* 自定义品牌色 */
:root {
  --vp-c-brand-1: #42b983;
  --vp-c-brand-2: #35a069;
  --vp-c-brand-3: #299e5c;
}
/* 代码块样式优化 */
.vp-code-group {
  margin: 16px 0;
}
/* 表格样式 */
.vp-doc table {
  display: block;
  overflow-x: auto;
}
2.2 注册全局组件
// docs/.vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme'
import './custom.css'
export default {
  extends: DefaultTheme,
  enhanceApp({ app }) {
    // 自动注册所有示例组件
    const examples = import.meta.glob('../../components/**/*.vue', {
      eager: true,
    })
    for (const path in examples) {
      const component = examples[path].default
      const name = path.match(/\/([^/]+)\.vue$/)?.[1]
      if (name) {
        app.component(name, component)
      }
    }
  },
}
🎭 第三步:交互式组件示例
3.1 创建 Demo 容器组件
<!-- docs/.vitepress/theme/components/DemoBlock.vue -->
<template>
  <div class="demo-block">
    <div class="demo-preview">
      <slot name="demo" />
    </div>
    <div class="demo-actions">
      <button @click="toggleCode" class="toggle-code-btn">
        {{ showCode ? '隐藏代码' : '查看代码' }}
      </button>
    </div>
    <div v-show="showCode" class="demo-code">
      <slot name="code" />
    </div>
  </div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const showCode = ref(false)
const toggleCode = () => {
  showCode.value = !showCode.value
}
</script>
<style scoped>
.demo-block {
  border: 1px solid var(--vp-c-divider);
  border-radius: 8px;
  margin: 16px 0;
}
.demo-preview {
  padding: 24px;
  background: var(--vp-c-bg-soft);
}
.demo-actions {
  padding: 12px 16px;
  border-top: 1px solid var(--vp-c-divider);
  display: flex;
  justify-content: flex-end;
}
.toggle-code-btn {
  padding: 4px 12px;
  font-size: 14px;
  border: 1px solid var(--vp-c-brand-1);
  color: var(--vp-c-brand-1);
  background: transparent;
  border-radius: 4px;
  cursor: pointer;
  transition: all 0.3s;
}
.toggle-code-btn:hover {
  background: var(--vp-c-brand-1);
  color: white;
}
.demo-code {
  border-top: 1px solid var(--vp-c-divider);
}
</style>
3.2 Markdown 自定义容器插件
// docs/.vitepress/plugins/demo-container.ts
import MarkdownIt from 'markdown-it'
import container from 'markdown-it-container'
import { readFileSync } from 'fs'
import { resolve } from 'path'
export default (md: MarkdownIt) => {
  md.use(container, 'demo', {
    validate(params: string) {
      return params.trim().match(/^demo\s+(.*)$/)
    },
    render(tokens: any[], idx: number) {
      const token = tokens[idx]
      const info = token.info.trim().match(/^demo\s+(.*)$/)
      if (tokens[idx].nesting === 1) {
        // 开始标签
        const demoPath = info?.[1] || ''
        const filePath = resolve(__dirname, '../../components', `${demoPath}.vue`)
        const source = readFileSync(filePath, 'utf-8')
        return `
          <DemoBlock>
            <template #demo>
              <${demoPath.split('/').pop()} />
            </template>
            <template #code>
              
\`\`\`vue
${source}
\`\`\`
            </template>
          </DemoBlock>
          <div>
        `
      } else {
        // 结束标签
        return '</div>\n'
      }
    },
  })
}
3.3 在 config.ts 中注册插件
import demoContainer from './plugins/demo-container'
export default defineConfig({
  markdown: {
    config: md => {
      md.use(demoContainer)
    },
  },
})
📝 第四步:编写组件文档
4.1 创建示例组件
<!-- docs/components/button/basic.vue -->
<template>
  <div class="demo">
    <Button>默认按钮</Button>
    <Button type="primary">主要按钮</Button>
    <Button type="success">成功按钮</Button>
  </div>
</template>
<script setup lang="ts">
import { Button } from '@gdu-common/ui'
</script>
<style scoped>
.demo {
  display: flex;
  gap: 12px;
}
</style>
4.2 编写文档
<!-- docs/components/button.md -->
# Button 按钮
常用的操作按钮。
## 基础用法
最简单的按钮用法。
:::demo button/basic
:::
## API
### Props
| 属性     | 说明     | 类型                                  | 默认值      |
| -------- | -------- | ------------------------------------- | ----------- |
| type     | 按钮类型 | `'default' \| 'primary' \| 'success'` | `'default'` |
| size     | 按钮尺寸 | `'small' \| 'medium' \| 'large'`      | `'medium'`  |
| loading  | 加载状态 | `boolean`                             | `false`     |
| disabled | 禁用状态 | `boolean`                             | `false`     |
### Events
| 事件名 | 说明           | 回调参数                      |
| ------ | -------------- | ----------------------------- |
| click  | 点击按钮时触发 | `(event: MouseEvent) => void` |
### Slots
| 插槽名  | 说明       |
| ------- | ---------- |
| default | 按钮内容   |
| icon    | 自定义图标 |
效果:
- 📖 文档和代码在一起,易于维护
- 🎨 实时渲染的组件示例
- 💻 可以查看源代码
- 📱 响应式设计
🎨 第五步:首页定制
5.1 英雄区域
## <!-- docs/index.md -->
layout: home
hero:
name: GDU Common
text: 企业级前端通用组件库
tagline: 基于 Vue 3 + TypeScript + Vite
image:
src: /logo.svg
alt: GDU Common
actions: - theme: brand
text: 快速开始
link: /guide/getting-started - theme: alt
text: 查看组件
link: /components/ - theme: alt  
 text: GitHub
link: https://github.com/your-org/gdu-common
---
5.2 特性展示
features:
- icon: 🎨
  title: Vue 3 组件库
  details: 基于 Vue 3 Composition API 开发,提供丰富且高质量的 UI 组件
  link: /components/
  linkText: 查看组件
- icon: ⚡
  title: Vite & Turborepo
  details: 使用 Vite 极速构建,Turborepo 智能缓存,构建速度提升 19 倍
- icon: 🔧
  title: TypeScript 优先
  details: 完整的类型定义和智能提示,提供一流的开发体验
- icon: 📦
  title: Monorepo 架构
  details: 使用 pnpm workspace + Turborepo 管理,支持多包开发和发布
5.3 自定义样式
<!-- docs/index.md -->
<style>
:root {
  --vp-home-hero-name-color: transparent;
  --vp-home-hero-name-background: linear-gradient(-45deg, #bd34fe 30%, #41d1ff);
  --vp-home-hero-image-background-image: linear-gradient(-45deg, #bd34fe50 50%, #47caff50);
  --vp-home-hero-image-filter: blur(44px);
}
.VPFeature {
  transition: all 0.3s;
}
.VPFeature:hover {
  transform: translateY(-4px);
  box-shadow: 0 12px 24px rgba(0, 0, 0, 0.1);
}
</style>
📱 第六步:高级功能
6.1 组件 Playground
<!-- docs/.vitepress/theme/components/Playground.vue -->
<template>
  <div class="playground">
    <div class="playground-preview">
      <component :is="currentComponent" v-bind="props" />
    </div>
    <div class="playground-controls">
      <h4>属性配置</h4>
      <div v-for="(value, key) in props" :key="key" class="control-item">
        <label>{{ key }}</label>
        <input v-model="props[key]" />
      </div>
    </div>
    <div class="playground-code">
      <pre><code>{{ generatedCode }}</code></pre>
    </div>
  </div>
</template>
<script setup lang="ts">
import { computed, reactive } from 'vue'
import { Button } from '@gdu-common/ui'
const props = reactive({
  type: 'primary',
  size: 'medium',
  loading: false,
})
const currentComponent = Button
const generatedCode = computed(() => {
  const propsStr = Object.entries(props)
    .map(([key, val]) => `${key}="${val}"`)
    .join(' ')
  return `<Button ${propsStr}>点击我</Button>`
})
</script>
6.2 API 表格自动生成
// docs/.vitepress/plugins/api-table.ts
import { readFileSync } from 'fs'
import { parse } from 'vue/compiler-sfc'
export function extractProps(filePath: string) {
  const source = readFileSync(filePath, 'utf-8')
  const { descriptor } = parse(source)
  // 解析 <script setup> 中的 defineProps
  const scriptContent = descriptor.script?.content || ''
  const propsMatch = scriptContent.match(/defineProps<(.+)>/)
  if (propsMatch) {
    // 提取 Props 类型定义
    return parsePropsType(propsMatch[1])
  }
  return []
}
6.3 暗黑模式支持
// config.ts
export default defineConfig({
  appearance: true, // 启用暗黑模式切换
  themeConfig: {
    // 暗黑模式下的logo
    logo: {
      light: '/logo-light.svg',
      dark: '/logo-dark.svg',
    },
  },
})
/* 暗黑模式样式 */
.dark .demo-block {
  border-color: var(--vp-c-divider);
  background: var(--vp-c-bg-soft);
}
🔧 第七步:Markdown 扩展
7.1 自定义容器
::: tip 提示
这是一个提示信息
:::
::: warning 警告
这是一个警告信息
:::
::: danger 危险
这是一个危险警告
:::
::: details 点击查看详情
这是详细内容
:::
7.2 代码组
::: code-group
\`\`\`bash [pnpm]
pnpm add @gdu-common/ui
\`\`\`
\`\`\`bash [npm]
npm install @gdu-common/ui
\`\`\`
\`\`\`bash [yarn]
yarn add @gdu-common/ui
\`\`\`
:::
7.3 代码高亮行
// 高亮特定行
\`\`\`typescript {2,4-6}
function hello() {
  const name = 'world'  // [!code highlight]
  console.log('line 4')  // [!code highlight]
  console.log('line 5')  // [!code highlight]
  console.log('line 6')  // [!code highlight]
}
\`\`\`
// 标记添加/删除
\`\`\`typescript
function hello() {
  const name = 'world'   // [!code --]
  const name = 'Vue 3'   // [!code ++]
}
\`\`\`
📊 第八步:SEO 优化
8.1 Meta 标签配置
export default defineConfig({
  head: [
    // SEO
    ['meta', { name: 'keywords', content: 'Vue3, 组件库, TypeScript, Monorepo' }],
    ['meta', { name: 'author', content: 'GDU Team' }],
    // Open Graph
    ['meta', { property: 'og:type', content: 'website' }],
    ['meta', { property: 'og:title', content: 'GDU Common' }],
    ['meta', { property: 'og:description', content: '企业级前端组件库' }],
    ['meta', { property: 'og:image', content: '/og-image.png' }],
    // Favicon
    ['link', { rel: 'icon', href: '/favicon.ico' }],
    ['link', { rel: 'apple-touch-icon', href: '/apple-touch-icon.png' }],
  ],
  // 最后更新时间
  lastUpdated: true,
  // 干净的 URL
  cleanUrls: true,
})
8.2 站点地图生成
// docs/.vitepress/config.ts
export default defineConfig({
  sitemap: {
    hostname: 'https://gdu-common.example.com',
  },
})
🚀 第九步:性能优化
9.1 构建优化
export default defineConfig({
  vite: {
    build: {
      // 代码压缩
      minify: 'terser',
      // 分包策略
      rollupOptions: {
        output: {
          manualChunks: {
            'vue-vendor': ['vue'],
            'vitepress-vendor': ['vitepress'],
          },
        },
      },
      // Chunk 大小警告
      chunkSizeWarningLimit: 1000,
    },
  },
})
9.2 图片优化
# 使用 webp 格式
docs/public/
├── logo.svg
├── hero.webp          # 首页图片
└── og-image.webp      # 社交分享图片
9.3 代码分割
// 异步加载组件
export default {
  async enhanceApp({ app }) {
    if (!import.meta.env.SSR) {
      const HeavyComponent = await import('./components/HeavyComponent.vue')
      app.component('HeavyComponent', HeavyComponent.default)
    }
  },
}
📦 第十步:部署配置
10.1 静态站点部署
export default defineConfig({
  base: '/', // 根目录部署
  // 或
  base: '/docs/', // 子目录部署
  outDir: '.vitepress/dist',
  cacheDir: '.vitepress/cache',
})
10.2 Nginx 配置
server {
    listen 80;
    server_name docs.gdu-common.com;
    root /var/www/docs/.vitepress/dist;
    index index.html;
    # SPA 路由支持
    location / {
        try_files $uri $uri/ /index.html;
    }
    # 静态资源缓存
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}
10.3 Docker 部署
# docs/Dockerfile
FROM node:20-alpine
WORKDIR /app
# 复制文件
COPY package.json pnpm-lock.yaml ./
COPY docs ./docs
COPY packages ./packages
# 安装依赖
RUN npm install -g pnpm
RUN pnpm install --frozen-lockfile
# 构建文档
RUN pnpm --filter @gdu-common/docs build
# 使用 nginx 服务
FROM nginx:alpine
COPY --from=0 /app/docs/.vitepress/dist /usr/share/nginx/html
COPY docs/nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
💡 最佳实践
1. 文档组织结构
docs/
├── guide/                 # 指南(给新手)
│   ├── index.md          # 介绍
│   ├── getting-started.md # 快速开始
│   └── installation.md   # 安装
├── components/            # 组件文档(给使用者)
│   ├── index.md
│   └── button.md
├── utils/                 # 工具文档
│   ├── index.md
│   └── common.md
└── advanced/              # 高级用法(给进阶用户)
    ├── customization.md
    └── theme.md
2. 示例代码规范
<!-- ✅ 好的示例 -->
<template>
  <!-- 清晰的HTML结构 -->
  <div class="demo">
    <Button @click="handleClick"> 点击次数:{{ count }} </Button>
  </div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Button } from '@gdu-common/ui'
// 简洁的逻辑
const count = ref(0)
const handleClick = () => {
  count.value++
}
</script>
<style scoped>
/* 必要的样式 */
.demo {
  padding: 20px;
}
</style>
3. 文档写作规范
# ✅ 清晰的标题层级
# 组件名
## 基础用法
### 示例标题
#### 细节说明
# ✅ 代码示例在前,文字说明在后
:::demo button/basic
:::
按钮组件支持多种类型...
# ✅ 完整的 API 文档
## Props
## Events
## Slots
## Methods
📈 效果展示
构建性能
# 文档构建速度
pnpm --filter @gdu-common/docs build
vitepress v1.6.4
✓ building client + server bundles...
✓ rendering pages...
build complete in 4.3s
访问性能
| 指标 | 数值 | 说明 | 
|---|---|---|
| FCP | 0.8s | 首次内容绘制 | 
| LCP | 1.2s | 最大内容绘制 | 
| TTI | 1.5s | 可交互时间 | 
| 总分 | 95/100 | Lighthouse 性能分数 | 
用户体验
- ✅ 本地搜索(无需服务器)
- ✅ 暗黑模式切换
- ✅ 响应式设计
- ✅ 代码一键复制
- ✅ 交互式示例
🤔 常见问题
Q1: 如何在文档中引用组件?
// 方式1:配置 alias
vite: {
  resolve: {
    alias: {
      '@gdu-common/ui': resolve(__dirname, '../../packages/ui/src')
    }
  }
}
// 方式2:直接导入
import { Button } from '@gdu-common/ui'
Q2: 如何实现组件的在线编辑?
// 集成 @vue/repl
import { Repl } from '@vue/repl'
// 提供在线编辑环境
<Repl :store="store" />
Q3: 如何自动生成 API 文档?
# 使用 vue-docgen-api
pnpm add -D vue-docgen-api
# 解析组件生成 JSON
# 渲染成 Markdown 表格
Q4: 文档部署后 404 怎么办?
// 配置正确的 base
export default defineConfig({
  base: '/', // 根目录
  // 或
  base: '/docs/', // 子目录
  cleanUrls: true, // 干净的 URL
})
🎁 完整示例
目录结构
docs/
├── .vitepress/
│   ├── config.ts
│   ├── theme/
│   │   ├── index.ts
│   │   ├── components/
│   │   │   ├── DemoBlock.vue
│   │   │   └── Playground.vue
│   │   └── custom.css
│   └── plugins/
│       └── demo-container.ts
├── components/
│   ├── button/
│   │   ├── index.md
│   │   ├── basic.vue
│   │   ├── types.vue
│   │   └── loading.vue
│   └── index.md
├── guide/
│   ├── index.md
│   └── getting-started.md
├── public/
│   ├── logo.svg
│   └── favicon.ico
├── index.md
└── package.json
🎉 总结
VitePress 文档站点的核心价值:
技术实现
- ✅ VitePress 配置和自定义主题
- ✅ 交互式组件示例
- ✅ 自动代码提取和高亮
- ✅ SEO 和性能优化
实际效果
- 📖 专业级文档体验
- ⚡ 4.3s 构建,1.2s 加载
- 🎨 美观的 UI 设计
- 📱 完美的响应式
关键配置
pnpm doc:dev     # 开发
pnpm doc:build   # 构建
pnpm doc:preview # 预览
下一篇文章,我将分享 CI/CD 自动化流程,包括:
- GitLab CI 配置
- 自动化测试
- 自动化发布
- Docker 镜像构建
🔗 系列文章
- 📖 上一篇: 版本管理实战:Changeset 工作流完全指南
- 📖 下一篇: 《CI/CD 自动化:GitLab CI 完整流程》
- 🏠 专栏首页: 从零到一:构建现代化企业级 Monorepo 项目实战
文档站点搭建完成!觉得有帮助的话点个赞支持一下! 👍
你的文档站点用的什么工具?有什么优化技巧?评论区交流! 💬
