组件 API 文档显示源码指南
本文档说明如何在 VitePress 文档中同时显示 React 组件的预览和源码。
快速开始
1. 基本使用
在你的组件文档 .md 文件中使用 CodePreview 组件:
vue
<script setup>
import ButtonDemo from '../../demos/button-basic.tsx'
import ButtonDemoSource from '../../demos/button-basic.tsx?raw'
import CodePreview from '../../.vitepress/theme/components/CodePreview.vue'
</script>
<CodePreview :component="ButtonDemo" :source="ButtonDemoSource" :default-show-code="false" />2. 参数说明
CodePreview 组件参数
| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
component | Component | 必需 | React 组件(直接导入) |
source | string | - | 组件源码字符串 |
default-show-code | boolean | false | 是否默认显示源码 |
3. 工作原理
导入方式
javascript
// 导入 React 组件(用于渲染)
import ButtonDemo from '../../demos/button-basic.tsx'
// 导入源码文本(用于显示)- 注意 ?raw 后缀
import ButtonDemoSource from '../../demos/button-basic.tsx?raw'?raw查询参数会触发自定义 Vite 插件- 插件会读取源文件内容并作为字符串导出
- 源码字符串通过 Vue 的
props传递给组件 - 组件内部进行 HTML 转义和语法高亮
功能特性
1. 可折叠源码
- 默认只显示组件预览
- 点击"显示代码"按钮展开源码
- 点击"隐藏代码"按钮收起源码
2. 代码复制
- 在显示源码时,右上角会出现"复制代码"按钮
- 点击后会复制完整源码到剪贴板
- 复制成功后按钮文本会短暂显示"已复制!"
3. 主题适配
- 自动适配浅色/深色主题
- 使用 VitePress 的 CSS 变量
- 保持与文档样式一致
完整示例
创建一个新组件文档:
markdown
# MyComponent 我的组件
组件描述...
## 示例
<script setup>
import MyDemo from '../../demos/my-component-demo.tsx'
import MyDemoSource from '../../demos/my-component-demo.tsx?raw'
import CodePreview from '../../.vitepress/theme/components/CodePreview.vue'
</script>
<CodePreview :component="MyDemo" :source="MyDemoSource" :default-show-code="false" />
## API 参数
| 参数 | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| ... | ... | ... | ... |技术实现
1. Vite 插件 (demo-source.ts)
typescript
// .vitepress/plugins/demo-source.ts
import fs from 'fs'
import { Plugin } from 'vite'
export function demoSourcePlugin(): Plugin {
return {
name: 'vitepress-demo-source',
enforce: 'pre',
transform(code, id) {
// 处理 ?raw 查询参数
if (id.includes('?raw') && id.includes('/demos/')) {
const filePath = id.split('?')[0]
const source = fs.readFileSync(filePath, 'utf-8')
return {
code: \`export default \${JSON.stringify(source)}\`,
map: null
}
}
return null
}
}
}2. CodePreview 组件
位于 .vitepress/theme/components/CodePreview.vue
- 使用 Vue 3 Composition API
- 使用 React 18 的
createRootAPI - 支持组件挂载和卸载的生命周期管理
3. 配置集成
在 .vitepress/config.ts 中注册插件:
typescript
import { demoSourcePlugin } from './plugins/demo-source'
export default defineConfig({
vite: {
plugins: [demoSourcePlugin()],
// ...其他配置
}
})在 .vitepress/theme/index.ts 中注册组件:
typescript
import CodePreview from './components/CodePreview.vue'
export default {
extends: DefaultTheme,
enhanceApp({ app }) {
app.component('CodePreview', CodePreview)
// ...其他组件
}
}样式定制
可以通过修改 CodePreview.vue 中的样式来定制外观:
css
.code-preview {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
/* 自定义样式... */
}可用的 VitePress CSS 变量:
--vp-c-bg- 背景色--vp-c-divider- 分割线颜色--vp-c-text-1- 主文本颜色--vp-c-brand- 品牌色--vp-code-block-bg- 代码块背景
常见问题
Q: 为什么要使用 ?raw 而不是直接读取文件?
A: 使用 ?raw 可以利用 Vite 的模块系统,获得以下好处:
- HMR (热模块替换) 支持
- 构建时优化
- 模块缓存
Q: 可以显示多个代码示例吗?
A: 可以!只需要多次使用 CodePreview 组件:
vue
<script setup>
import Demo1 from '../../demos/demo1.tsx'
import Demo1Source from '../../demos/demo1.tsx?raw'
import Demo2 from '../../demos/demo2.tsx'
import Demo2Source from '../../demos/demo2.tsx?raw'
import CodePreview from '../../.vitepress/theme/components/CodePreview.vue'
</script>
<CodePreview :component="Demo1" :source="Demo1Source" />
<CodePreview :component="Demo2" :source="Demo2Source" />Q: 如何默认展开源码?
A: 设置 default-show-code 为 true:
vue
<CodePreview
:component="ButtonDemo"
:source="ButtonDemoSource"
:default-show-code="true"
/>Q: 支持 TypeScript 高亮吗?
A: 支持!VitePress 使用 Shiki 进行代码高亮,自动识别 tsx 代码块。
迁移指南
从旧的 ReactDemo 方式迁移
旧方式:
vue
<script setup>
import ButtonDemo from '../../demos/button-basic.tsx'
import { defineComponent, h, onMounted, onBeforeUnmount } from 'vue'
import { createRoot } from 'react-dom/client'
import React from 'react'
const ReactDemo = defineComponent({
name: 'ReactDemo',
setup() {
let root = null
let container = null
onMounted(() => {
container = document.createElement('div')
document.querySelector('#button-demo').appendChild(container)
root = createRoot(container)
root.render(React.createElement(ButtonDemo))
})
onBeforeUnmount(() => {
if (root) root.unmount()
if (container && container.parentNode) {
container.parentNode.removeChild(container)
}
})
return () => h('div', { id: 'button-demo' })
}
})
</script>
<ReactDemo />新方式:
vue
<script setup>
import ButtonDemo from '../../demos/button-basic.tsx'
import ButtonDemoSource from '../../demos/button-basic.tsx?raw'
import CodePreview from '../../.vitepress/theme/components/CodePreview.vue'
</script>
<CodePreview :component="ButtonDemo" :source="ButtonDemoSource" :default-show-code="false" />优势:
- 代码量减少 80%+
- 自动显示源码
- 支持代码折叠/展开
- 内置复制功能
- 更好的用户体验
下一步
- 为所有组件文档添加
CodePreview - 根据需要自定义样式
- 考虑添加更多功能(如代码编辑、在线运行等)