VitePress 文档站点:打造专业级组件文档(含交互式示例)

本文是《从零到一:构建现代化企业级 Monorepo 项目实战》系列的第六篇。这篇文章将教你如何搭建一个媲美官方文档的专业文档站点。

🎯 本文目标

  • VitePress 在 Monorepo 中的集成
  • 组件示例自动导入和渲染
  • 自定义主题和样式
  • 文档站点性能优化
  • 部署配置

📖 为什么选择 VitePress?

文档工具对比

工具 性能 Vue 支持 Markdown 扩展 学习成本 推荐指数
VuePress ⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐
Docusaurus ⭐⭐⭐ ❌ React ⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐
Docsify ⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐
VitePress ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐

VitePress 优势:

  • ⚡ 基于 Vite,开发和构建速度极快
  • 🎨 Vue 组件可以直接在 Markdown 中使用
  • 📝 Markdown 扩展能力强大
  • 🎯 专为 Vue 生态设计

🏗️ 项目结构设计

bash 复制代码
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

bash 复制代码
cd docs
pnpm init
pnpm add -D vitepress vue

1.2 基础配置

typescript 复制代码
// 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 添加脚本

json 复制代码
// docs/package.json
{
  "scripts": {
    "dev": "vitepress dev",
    "build": "vitepress build",
    "preview": "vitepress preview"
  }
}

🎨 第二步:自定义主题

2.1 创建自定义样式

css 复制代码
/* 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 注册全局组件

typescript 复制代码
// 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 容器组件

vue 复制代码
<!-- 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 自定义容器插件

typescript 复制代码
// 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 中注册插件

typescript 复制代码
import demoContainer from './plugins/demo-container'

export default defineConfig({
  markdown: {
    config: md => {
      md.use(demoContainer)
    },
  },
})

📝 第四步:编写组件文档

4.1 创建示例组件

vue 复制代码
<!-- 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 编写文档

markdown 复制代码
<!-- 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 英雄区域

markdown 复制代码
## <!-- 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 特性展示

markdown 复制代码
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 自定义样式

vue 复制代码
<!-- 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

vue 复制代码
<!-- 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 表格自动生成

typescript 复制代码
// 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 暗黑模式支持

typescript 复制代码
// config.ts
export default defineConfig({
  appearance: true, // 启用暗黑模式切换

  themeConfig: {
    // 暗黑模式下的logo
    logo: {
      light: '/logo-light.svg',
      dark: '/logo-dark.svg',
    },
  },
})
css 复制代码
/* 暗黑模式样式 */
.dark .demo-block {
  border-color: var(--vp-c-divider);
  background: var(--vp-c-bg-soft);
}

🔧 第七步:Markdown 扩展

7.1 自定义容器

markdown 复制代码
::: tip 提示
这是一个提示信息
:::

::: warning 警告
这是一个警告信息
:::

::: danger 危险
这是一个危险警告
:::

::: details 点击查看详情
这是详细内容
:::

7.2 代码组

markdown 复制代码
::: 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 复制代码
// 高亮特定行
\`\`\`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 标签配置

typescript 复制代码
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 站点地图生成

typescript 复制代码
// docs/.vitepress/config.ts
export default defineConfig({
  sitemap: {
    hostname: 'https://gdu-common.example.com',
  },
})

🚀 第九步:性能优化

9.1 构建优化

typescript 复制代码
export default defineConfig({
  vite: {
    build: {
      // 代码压缩
      minify: 'terser',

      // 分包策略
      rollupOptions: {
        output: {
          manualChunks: {
            'vue-vendor': ['vue'],
            'vitepress-vendor': ['vitepress'],
          },
        },
      },

      // Chunk 大小警告
      chunkSizeWarningLimit: 1000,
    },
  },
})

9.2 图片优化

bash 复制代码
# 使用 webp 格式
docs/public/
├── logo.svg
├── hero.webp          # 首页图片
└── og-image.webp      # 社交分享图片

9.3 代码分割

typescript 复制代码
// 异步加载组件
export default {
  async enhanceApp({ app }) {
    if (!import.meta.env.SSR) {
      const HeavyComponent = await import('./components/HeavyComponent.vue')
      app.component('HeavyComponent', HeavyComponent.default)
    }
  },
}

📦 第十步:部署配置

10.1 静态站点部署

typescript 复制代码
export default defineConfig({
  base: '/', // 根目录部署
  // 或
  base: '/docs/', // 子目录部署

  outDir: '.vitepress/dist',
  cacheDir: '.vitepress/cache',
})

10.2 Nginx 配置

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 部署

dockerfile 复制代码
# 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. 文档组织结构

bash 复制代码
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. 示例代码规范

vue 复制代码
<!-- ✅ 好的示例 -->
<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. 文档写作规范

markdown 复制代码
# ✅ 清晰的标题层级

# 组件名

## 基础用法

### 示例标题

#### 细节说明

# ✅ 代码示例在前,文字说明在后

:::demo button/basic
:::

按钮组件支持多种类型...

# ✅ 完整的 API 文档

## Props

## Events

## Slots

## Methods

📈 效果展示

构建性能

bash 复制代码
# 文档构建速度
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: 如何在文档中引用组件?

typescript 复制代码
// 方式1:配置 alias
vite: {
  resolve: {
    alias: {
      '@gdu-common/ui': resolve(__dirname, '../../packages/ui/src')
    }
  }
}

// 方式2:直接导入
import { Button } from '@gdu-common/ui'

Q2: 如何实现组件的在线编辑?

typescript 复制代码
// 集成 @vue/repl
import { Repl } from '@vue/repl'

// 提供在线编辑环境
<Repl :store="store" />

Q3: 如何自动生成 API 文档?

bash 复制代码
# 使用 vue-docgen-api
pnpm add -D vue-docgen-api

# 解析组件生成 JSON
# 渲染成 Markdown 表格

Q4: 文档部署后 404 怎么办?

typescript 复制代码
// 配置正确的 base
export default defineConfig({
  base: '/', // 根目录
  // 或
  base: '/docs/', // 子目录

  cleanUrls: true, // 干净的 URL
})

🎁 完整示例

目录结构

arduino 复制代码
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 设计
  • 📱 完美的响应式

关键配置

bash 复制代码
pnpm doc:dev     # 开发
pnpm doc:build   # 构建
pnpm doc:preview # 预览

下一篇文章,我将分享 CI/CD 自动化流程,包括:

  • GitLab CI 配置
  • 自动化测试
  • 自动化发布
  • Docker 镜像构建

🔗 系列文章


文档站点搭建完成!觉得有帮助的话点个赞支持一下! 👍

你的文档站点用的什么工具?有什么优化技巧?评论区交流! 💬

相关推荐
甜瓜看代码4 小时前
666
前端
吃饺子不吃馅4 小时前
【八股汇总,背就完事】这一次再也不怕webpack面试了
前端·面试·webpack
Amos_Web4 小时前
Rust实战教程--文件管理命令行工具
前端·rust·全栈
li理4 小时前
鸿蒙相机开发入门篇(官方实践版)
前端
webxin6664 小时前
页面动画和延迟加载动画的实现
前端·javascript
逛逛GitHub5 小时前
这个牛逼的股票市场平台,在 GitHub 上开源了。
前端·github
m0_651593915 小时前
Netty网络架构与Reactor模式深度解析
网络·架构
细节控菜鸡5 小时前
【排查实录】Web 页面能打开,服务器能通接口,客户端却访问失败?原因全在这!
运维·服务器·前端
今天头发还在吗5 小时前
React + Ant Design 日期选择器避免显示“Invalid Date“的解决方案
前端·react.js·前端框架·ant design