VitePress — Vue 团队用 Vite 写的静态文档站点生成器
是什么
VitePress 是把 markdown 直接变成静态 HTML 站点的工具,专门给”写文档”这件事设计,由 Vue 团队维护。日常类比:像一个会自己排版的印刷机——你写文字稿(markdown),它帮你印成一本电子书(带导航、搜索、代码高亮的网站)。
你写:
# 我的文档这里是一段说明,下面有代码:```jsconsole.log('hello')跑一行 `npx vitepress build`,VitePress 把这份 .md 转成 .html,附上侧边栏、深色模式、代码高亮、自动生成的目录——一个标准文档站就出来了。
Vue 自己的官网、Pinia、Vitest、VueUse 的文档全部用 VitePress 写——**它就是 Vue 生态脚下的那块地板**。
## 为什么重要
不理解 VitePress 长啥样,下面这些事都会困惑:
- 为什么 docs 站点首屏那么轻——它把"99% 是静态 HTML"这个事实做到了字节级别- 为什么写文档可以**直接在 markdown 里塞 Vue 组件**——不用 MDX 那种 JSX-flavored 语法- 为什么 Vue 团队会做 Astro / Docusaurus / Nextra 之外的第四种选择——心智不同- 为什么 dev server 启动几乎瞬时——Vite 的 ESM 原生 + HMR 已经赢了
## 核心要点
VitePress 把"docs 站点"拆成 **三个心脏**:
1. **markdown 转换器**:底下用社区成熟的 markdown-it(async 分支),上面挂 17 个插件按顺序拼出"VitePress 风味"——`:::tip`、`<<< @/file.ts` 引用、shiki 代码高亮、GitHub Alerts 这些都是插件做的。类比:一条流水线,原料从一头进去,每个工位贴一个加工。
2. **双 bundle SSG 构建**:跑两次 Vite build——一次 **client**(输出浏览器要的 JS/CSS),一次 **server**(输出 SSR render 函数)。同一份源码经过 Vite plugin 自动变两套产物,VitePress 自己不写 SSG 引擎,把活外包给 Vite。
3. **lean + full 双客户端 bundle**:第一次访问页面只下载 `.lean.js`(hydration 必需的最小代码),从这页跳走再回来才下载 `.full.js`(带完整组件交互逻辑)。类比:进门只发名片,要谈合作再拿合同——首屏 JS 减 30-50%。
三件加起来才 ~6k 行 TypeScript(不含默认主题的 .vue),是"框架做减法"的代表作。
## 实践案例
### 案例 1:60 秒起一个 docs 站
```bashmkdir my-docs && cd my-docsnpm init -ynpm install -D vitepressnpx vitepress init# 选 ./docs / Site title: Test / Default themenpm run docs:dev # 起 dev server 在 http://localhost:5173npm run docs:build # 输出 docs/.vitepress/distls docs/.vitepress/dist/assets/chunks/# index.<hash>.js index.<hash>.lean.js ← 双 bundle 验证hashmap.json 是每页 → 内容 hash 的映射,给”用户停在旧版本,你刚部署新版本”做兜底重拉。
案例 2:在 markdown 里直接用 Vue 组件
.vitepress/theme/index.ts:
import DefaultTheme from 'vitepress/theme'import Counter from './Counter.vue'
export default { extends: DefaultTheme, enhanceApp({ app }) { app.component('Counter', Counter) }}然后任何 .md 里直接 <Counter :start="3" /> 就能用。enhanceApp 是用户唯一能”插进 Vue runtime”的扩展点——错过它就只能改默认主题源码。
案例 3:自己写 markdown-it 插件加新语法
.vitepress/config.ts:
export default defineConfig({ markdown: { config(md) { md.use((md) => { md.core.ruler.push('my-marker', (state) => { // 自定义 token 处理 }) }) } }})config(md) 在 VitePress 自己 17 个插件之后跑(用户最后说话),还有个 preConfig(md) 在所有插件之前跑(少见场景)。
踩过的坑
-
lean / full 双 bundle 的切换由
isInitialPageLoad驱动:这个标志在client/app/index.ts模块级,只在inBrowser时重置——SSR 自定义 runner 复用createApp时不会清零,后续路由会一直拿 lean 失败 -
createMarkdownRenderer是单例:if (md) return md让同一 node 进程只创一个实例。monorepo nx 缓存复用 node worker 时,shiki 主题切换可能不生效——只在 dev server 重启时调disposeMdItInstance() -
17 个 markdown-it 插件的注册顺序就是语义:
componentPlugin必须最先(识别 Vue 组件标记,避免被 markdown 转义成<p>),snippetPlugin必须在preWrapperPlugin之前——颠倒会让代码块外壳包错 -
enhanceApp钩子很容易漏写:用户经常把全局组件直接 import 到.md文件,导致 SSR 报错window is not defined——必须在enhanceApp里app.component()注册,VitePress 会自动包<ClientOnly>兜底
适用 vs 不适用场景
适用:
- Vue 生态的产品 / 库文档(与 Vue 组件天然衔接)
- markdown 占 80% 内容、需要少量交互的技术博客
- 想要极致首屏性能的 docs 站(lean bundle 拍平了 SPA framework 的开销)
- 对 dev 体验敏感的写作场景(HMR 毫秒级回显)
不适用:
- React / Solid / Svelte 生态项目 → 用 Docusaurus / Nextra / Starlight
- 内容动态化重的站点(需要 CMS / SSR fetch)→ 用 Nuxt / Next
- 不写 markdown 的纯 SPA → 直接用 Vite + Vue
- 需要 MDX 那种 JSX-in-markdown 的项目 → VitePress 用的是 SFC 风格不是 JSX
历史小故事(可跳过)
- 2018 年:尤雨溪写 VuePress(webpack 底,Vue 2),Vue 官网在它身上痛苦了 4 年——启动慢、HMR 重、依赖一长串自家 plugin
- 2020 年:尤雨溪做 Vite(“另起炉灶比改 webpack 容易”),同年 11 月 Vite 1.0 发布
- 2021 年:顺手起了 VitePress 这个”用 Vite 重写 VuePress”的实验项目——本意是给 Vue 3 文档用
- 2024-03:VitePress 1.0 正式发布,成为 Vue 生态官方推荐
- 2026 年:当前 2.0-alpha.17,brc-dd 接手主维护(979 commit),尤雨溪退到 second commiter(508 commit)
学到什么
- 框架的最高境界是做减法——VitePress 主仓只 ~6k 行 TS,因为 markdown 转换、build、SSR 全部外包给社区成熟工具
- “双 bundle”是 docs 站点的关键优化——lean 给首屏,full 给交互,这个分流让 docs 站点首屏字节数掉到一个量级
- markdown 转 Vue SFC 而不是发明新语法——这是 VitePress 与 MDX 的根本分歧。Vue SFC 已经是用户熟悉的容器,没必要再学 JSX-in-markdown
- 插件顺序即语义:17 个 markdown-it 插件的注册顺序决定了语法解析结果。顺序是 API 的一部分
延伸阅读
- 官方文档:vitepress.dev(用 VitePress 自己写的,吃自己狗粮)
- 设计哲学博客:What is VitePress?
- v1.0 发布说明:Releases v1.0.0
- 视频教程:Anthony Fu — VitePress in 100 Seconds
- vite —— VitePress 跑的两次 build 都依赖 Vite 这台引擎
- vue —— 浏览器 runtime 的 SFC 编译与 hydration
关联
- vite —— Vite 提供 dev server / 双 build / Vite plugin 接口,VitePress 是 Vite 的”应用层用户”
- vue —— VitePress 用 Vue 做 client runtime,markdown 转出来的就是 Vue SFC
- markdown-it —— VitePress 的 markdown 内核,17 个插件全挂在它上面
- shiki —— 代码高亮引擎,VitePress 用 shiki 双主题(light / dark)做语法着色
- starlight —— Astro 生态的对应物,思路相似(markdown + 组件 + SSG)但底层引擎不同
- docusaurus —— React 生态的对应物,Meta 维护,更重但功能更全
- nextra —— Next.js 生态的对应物,MDX-first,VitePress 选了相反的 SFC-first 路线
- rolldown —— Rust 写的 Rollup 替代品,VitePress 已支持 rolldown-vite
反向链接
- astro —— Astro — 内容站点优先的 Web 框架
- chalk —— chalk — 让 console.log 输出彩色字符串的 Node 库
- dayjs —— Day.js — 用 2 KB 复刻 Moment 的极简日期库
- docusaurus —— Docusaurus — 一组 plugin 协作出来的文档站框架
- echarts —— Apache ECharts — 给一个 JSON 就能画图的可视化库
- markdown-it —— markdown-it — 把 Markdown 文本变成 HTML 的工业级解析器
- nextra —— Nextra — 在 Next.js 上盖一层文档站脚手架
- rolldown —— rolldown — 用 Rust 给 Vite 当统一引擎的打包器
- shiki —— shiki — 把 VS Code 那套染色搬到网页上
- starlight —— Starlight — Astro 文档站点主题
- vite —— Vite — 浏览器自己加载源码的构建工具
- vue —— Vue.js — 渐进式 UI 框架
- web-vitals —— web-vitals — 让你在自己页面测的数和 Google 排名用的数对得上