主题
MkDocs 迁移到 VitePress
银河项目文档迁移步骤,按顺序执行即可。
第一步:创建 VitePress 项目
⚠️ 请用官方方式,不要用
npm create vitepress@latest(第三方旧脚手架,版本过旧)。
powershell
mkdir 手册文档
cd 手册文档
npm add -D vitepress@latest vue@latest
npx vitepress init向导建议:
| 选项 | 建议 |
|---|---|
| 文档目录 | docs |
| TypeScript | 是 |
| 脚本前缀 | 留空 |
完成后 package.json 大致如下(含 cross-env 与多环境 build 脚本):
json
{
"type": "module",
"scripts": {
"dev": "vitepress dev docs",
"build": "cross-env VP_BASE=/docs/ vitepress build docs",
"build:root": "cross-env VP_BASE=/ vitepress build docs",
"serve": "vitepress serve docs"
},
"devDependencies": {
"cross-env": "^7.0.3",
"vitepress": "^1.6.0",
"vue": "^3.4.0"
}
}启动预览:
powershell
yarn install
yarn dev浏览器访问:http://localhost:5173
第二步:迁移 Markdown 文件
把 MkDocs 项目 docs/ 下的内容复制到 VitePress 的 docs/:
docs/
├── index.md ← 首页
├── guide/
│ ├── longmai.md
│ ├── kylin-pack.md
│ └── ...
└── guide/imgs/ ← 图片一并复制注意:
.md文件内容基本可直接复用- 图片路径
./imgs/xxx.png一般不用改 - 侧栏链接用 URL 路径(如
/guide/longmai),不是guide/longmai.md
第三步:编写配置文件
创建 / 编辑 docs/.vitepress/config.ts:
ts
import { defineConfig } from 'vitepress'
function resolveBase(): string {
const raw = process.env.VP_BASE?.trim()
if (!raw || raw === '/') return '/'
const name = raw.replace(/^\/+|\/+$/g, '')
return name ? `/${name}/` : '/'
}
export default defineConfig({
title: '银河项目文档',
description: '银河项目文档',
lang: 'zh-CN',
base: resolveBase(),
themeConfig: {
siteTitle: '银河项目文档',
nav: [
{ text: '首页', link: '/' },
{ text: '银河项目', link: '/guide/longmai' },
],
sidebar: [
{
text: '银河项目',
collapsed: false,
items: [
{ text: '手册介绍', link: '/' },
{ text: '龙脉加密狗制作', link: '/guide/longmai' },
{ text: '银河麒麟打包', link: '/guide/kylin-pack' },
{ text: '银河(Linux)安装', link: '/guide/linux-install' },
{ text: '银河plc点位', link: '/guide/yh-plc-points' },
{ text: 'VitePress 迁移指南', link: '/guide/vitepress' },
],
},
],
outline: {
level: [2, 3],
label: '本页目录',
},
docFooter: {
prev: '上一页',
next: '下一页',
},
search: {
provider: 'local',
options: {
locales: {
root: {
translations: {
button: {
buttonText: '搜索文档',
buttonAriaLabel: '搜索文档',
},
modal: {
noResultsText: '没有找到结果',
resetButtonTitle: '清除',
},
},
},
},
},
},
footer: {
message: '银河项目内部文档',
copyright: 'Copyright © h.yw',
},
},
markdown: {
lineNumbers: true,
},
})第四步:配置自定义样式
4.1 创建 docs/.vitepress/theme/index.ts:
ts
import DefaultTheme from 'vitepress/theme'
import './custom.css'
export default DefaultTheme4.2 创建 docs/.vitepress/theme/custom.css:
css
:root {
--vp-sidebar-width: 220px;
--vp-layout-max-width: 1680px;
--vp-c-divider: #e5e7eb;
}
.dark {
--vp-c-divider: #3f3f46;
}
/* 150% 等效缩放:16px × 1.5 = 24px */
html {
font-size: 28px;
}
body {
font-size: 1rem;
line-height: 1.5;
}
/* 主题内仍写死 px 的正文元素,按 150% 同比放大 */
.vp-doc h1 {
font-size: 42px;
line-height: 1.3;
}
.vp-doc h2 {
font-size: 36px;
line-height: 1.35;
}
.vp-doc h3 {
font-size: 30px;
line-height: 1.4;
}
.vp-doc h4 {
font-size: 27px;
line-height: 1.4;
}
.vp-doc p,
.vp-doc li,
.vp-doc blockquote > p {
line-height: 1.75;
}
.vp-doc th,
.vp-doc td {
font-size: 0.875em;
}
/* 顶栏底部分割线 */
.VPNav,
.VPNavBar {
border-bottom: 1px solid var(--vp-c-divider);
}
/*
* 侧栏贴左固定宽度(覆盖官方 ≥1440px 居中留白逻辑)
*/
@media (min-width: 960px) {
.VPSidebar {
width: var(--vp-sidebar-width) !important;
padding: var(--vp-nav-height) 12px 96px 16px !important;
border-right: 1px solid var(--vp-c-divider);
background-color: var(--vp-c-bg-soft);
}
.VPNavBar.has-sidebar .title {
width: var(--vp-sidebar-width) !important;
padding: 0 12px 0 16px !important;
}
.VPContent.has-sidebar {
padding-left: var(--vp-sidebar-width) !important;
}
.VPNavBar.has-sidebar .content {
padding-left: var(--vp-sidebar-width) !important;
padding-right: 32px !important;
}
.VPNavBar.has-sidebar .divider {
padding-left: var(--vp-sidebar-width) !important;
}
.VPLocalNav.has-sidebar {
padding-left: var(--vp-sidebar-width) !important;
}
}
@media (min-width: 1440px) {
.VPSidebar {
width: var(--vp-sidebar-width) !important;
padding-left: 16px !important;
}
.VPNavBar.has-sidebar .title {
width: var(--vp-sidebar-width) !important;
padding-left: 16px !important;
}
.VPContent.has-sidebar {
padding-left: var(--vp-sidebar-width) !important;
padding-right: 0 !important;
}
.VPNavBar.has-sidebar .content {
padding-left: var(--vp-sidebar-width) !important;
padding-right: 32px !important;
}
.VPNavBar.has-sidebar .divider {
padding-left: var(--vp-sidebar-width) !important;
}
.VPLocalNav.has-sidebar {
padding-left: var(--vp-sidebar-width) !important;
}
}
/* 正文加宽 + 右侧本页目录缩小 */
@media (min-width: 1280px) {
.VPDoc.has-aside .content-container {
max-width: 960px !important;
}
.VPDoc.has-aside .content {
flex: 1;
min-width: 0;
max-width: none !important;
}
.VPDoc .aside {
max-width: 160px;
padding-left: 16px;
}
.VPDoc .aside-container,
.VPDoc .aside-curtain {
width: 140px;
}
.VPDoc .outline-title {
font-size: 12px;
line-height: 24px;
}
.VPDoc .outline-link {
font-size: 12px;
line-height: 24px;
}
}
/* 无右侧目录时(如首页 outline: false)也放宽正文 */
.VPDoc .content-container {
max-width: 960px;
}
/* 搜索移到右上角:导航 | 搜索 | 主题切换 */
@media (min-width: 768px) {
.VPNavBar .content-body > .menu {
order: 1;
}
.VPNavBar .content-body > .search {
flex-grow: 0 !important;
padding-left: 0 !important;
order: 2;
margin-left: 8px;
}
.VPNavBar .content-body > .appearance {
order: 3;
}
}
/* 首页文档卡片(固定 px,不受 html 字号影响;覆盖 .vp-doc a 链接样式) */
.doc-card-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 16px;
margin: 24px 0;
}
.vp-doc a.doc-card,
.vp-doc a.doc-card:hover {
display: block;
padding: 20px;
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
background-color: var(--vp-c-bg-soft);
text-decoration: none !important;
color: inherit !important;
font-weight: inherit;
transition: border-color 0.2s, box-shadow 0.2s;
}
.vp-doc a.doc-card:hover {
border-color: var(--vp-c-brand-1);
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
}
.dark .vp-doc a.doc-card:hover {
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3);
}
.vp-doc a.doc-card .doc-card-icon {
font-size: 24px;
line-height: 1;
}
.vp-doc a.doc-card h3 {
margin: 12px 0 8px !important;
font-size: 17.6px !important;
line-height: 1.4 !important;
font-weight: 600;
color: var(--vp-c-text-1) !important;
}
.vp-doc a.doc-card p {
margin: 0 !important;
font-size: 14.4px !important;
line-height: 1.6;
color: var(--vp-c-text-2) !important;
font-weight: 400;
}第五步:配置首页目录(卡片索引)
Docusaurus 可自动生成「分类 + 卡片网格」索引页;VitePress 无此内置功能,需手写 docs/index.md + CSS。
5.1 编辑 docs/index.md(链接用 相对路径 ./guide/xxx,便于 base: '/docs/' 子路径部署):
markdown
# 银河项目文档
我们整理了银河控制软件相关的安装、打包与加密狗制作等内部技术文档。可通过下方卡片或左侧目录进入具体章节。
## 文档目录
<div class="doc-card-list">
<a href="./guide/longmai" class="doc-card">
<span class="doc-card-icon">🔐</span>
<h3>龙脉加密狗制作</h3>
<p>龙脉加密狗制作工具使用说明,含到期时间设置与制作完成步骤。</p>
</a>
<a href="./guide/kylin-pack" class="doc-card">
<span class="doc-card-icon">📦</span>
<h3>银河麒麟打包</h3>
<p>银河麒麟版一键打包流程,上传编译程序、生成安装包与远程连接 Linux。</p>
</a>
<!-- 其余文档卡片同理,每新增一篇 guide 文档就加一张卡片 -->
</div>5.2 侧栏第一项建议加 { text: '手册介绍', link: '/' },与 Docusaurus 的 intro 页类似。
5.3 新增文档时记得同步三处:
| 位置 | 操作 |
|---|---|
docs/guide/xxx.md | 新建文档 |
config.ts → sidebar | 增加 { text: '...', link: '/guide/xxx' } |
docs/index.md | 增加一张 .doc-card 卡片 |
第六步:检查 Markdown 内容
迁移后逐页检查:
| 检查项 | 说明 |
|---|---|
| 侧栏链接 | link 路径与文件名一致,注意单复数 |
| 重复标题 | 同一页不要有两个 ## 目录,否则右侧大纲重复 |
| MkDocs 专有语法 | !!! note 改为 ::: info;Tab 语法需改写 |
| HTML 标题 | <h2 id="..."> 建议改为 ## 标题 |
提示框写法:
markdown
::: info 提示
这是提示内容
:::第七步:本地验证
powershell
yarn dev检查清单:
[ ] 所有侧栏页面能正常打开
[ ] 图片显示正常
[ ] 右上角搜索可用(Ctrl+K)
[ ] 右侧「本页目录」无重复
[ ] 侧栏贴左、布局正常
[ ] 首页卡片可点击、样式正常
第八步:构建与部署
构建
powershell
yarn install
yarn build # 默认 VP_BASE=/docs/,对应 Nginx html/docs/构建产物在 docs/.vitepress/dist/,将 dist 里的内容 复制到 Nginx 的 html/docs/(不是整包 dist 文件夹)。
powershell
# Windows 示例(按实际路径修改)
xcopy /E /Y docs\.vitepress\dist\* D:\nginx\html\docs\bash
# Linux 示例
rsync -av docs/.vitepress/dist/ /usr/share/nginx/html/docs/访问:http://你的IP/docs/
部署到其他子目录名
base 必须与 URL 子路径 一致(与 Nginx 磁盘文件夹名一致),通过环境变量 VP_BASE 在 打包时 指定,无需改源码:
powershell
# 部署到 html/test/ → 访问 http://IP/test/
cross-env VP_BASE=/test/ yarn vitepress build docs
# 部署到 html/yinhe/ → 访问 http://IP/yinhe/
cross-env VP_BASE=/yinhe/ yarn vitepress build docsLinux / macOS 可直接:
bash
VP_BASE=/test/ yarn vitepress build docs部署到网站根路径(html/ 根目录,访问 http://IP/):
powershell
yarn build:root| 命令 | VP_BASE | 复制目标 | 访问地址 |
|---|---|---|---|
yarn dev | /(默认) | — | http://localhost:5173/ |
yarn build | /docs/ | html/docs/ | http://IP/docs/ |
yarn build:root | / | html/ 根 | http://IP/ |
VP_BASE=/test/ yarn vitepress build docs | /test/ | html/test/ | http://IP/test/ |
注意: 换子目录名必须 重新 build,不能只改文件夹名。VitePress 不支持相对路径
base: './'。
第九步:Nginx 子目录部署说明
典型模型:root html;,多站点只需在 html/ 下建不同文件夹,不用改 Nginx 配置。
/usr/share/nginx/html/
├── docs/ ← yarn build 默认,访问 /docs/
├── test/ ← VP_BASE=/test/ build 后拷贝
└── other-site/为什么不能像 Vue SPA 那样用相对路径?
| Vue SPA(单页) | VitePress(文档站) | |
|---|---|---|
| 构建结果 | 通常 1 个 index.html | 多个 .html(每篇文档一个) |
| 路由 | 前端路由,不重新加载 HTML | 每个 URL 对应独立 HTML 文件 |
publicPath: './' | 只从入口页加载资源,换文件夹名可用 | ❌ 子页面会错路径,官方 不支持 相对 base |
VitePress 与 Docusaurus 一样,静态资源路径在 build 时写入 HTML/JS,因此:
- ❌ 不能
base: './'实现「任意文件夹名、零 rebuild」 - ✅ 用
VP_BASE打包时指定路径(不改 config 源码) - ✅ 或全团队统一子目录名(如都用
docs)
部署后无样式(CSS 404)
多半是 base 与访问路径不一致,例如 build 用 / 却访问 /docs/。重新用正确的 VP_BASE build 后再部署。
构建产物路径
| 项目 | 路径 |
|---|---|
| 构建输出 | docs/.vitepress/dist/(不是项目根 dist/) |
| 部署方式 | 拷贝 dist 里的内容 到 html/子目录/,不是拷贝 dist 文件夹本身 |
迁移经验与踩坑记录
以下为银河项目实际迁移过程中遇到的问题,下次新建或迁移站点时可对照检查。
初始化
| 问题 | 原因 | 处理 |
|---|---|---|
| 搜索按钮不显示 | 用了 npm create vitepress@latest(第三方 2022 脚手架),版本停在 alpha.28 | 改用 npm add -D vitepress + npx vitepress init |
| 搜索配置了仍无效 | search 写在根级而非 themeConfig 下 | 必须写在 themeConfig.search |
布局与样式
| 问题 | 原因 | 处理 |
|---|---|---|
| 搜索框在左上角 | 默认 flex-grow: 1 把搜索撑到左侧 | custom.css 中 flex-grow: 0 + order 调整 |
| 侧栏不贴左、中间大空白 | 只改 --vp-sidebar-width,未同步覆盖宽屏居中逻辑 | 同时覆盖 VPSidebar、VPContent、顶栏 padding-left(见第四步 CSS) |
侧栏 width 单独 !important | 与官方 ≥1440px 布局冲突 | 侧栏只改边框/背景,宽度用 :root { --vp-sidebar-width } |
内容与链接
| 问题 | 原因 | 处理 |
|---|---|---|
| 右侧「本页目录」重复 | 同一页两个 ## 目录 | 只保留一个,或改用列表不写 h2 |
| 侧栏页面 404 | link: '/guide/plc' 与文件名 yh-plc-points.md 不一致 | link 必须与 .md 文件名(无后缀)一致 |
HTML 标题 <h2 id="..."> | 大纲解析不稳定 | 优先用标准 Markdown ## 标题 |
部署
| 问题 | 原因 | 处理 |
|---|---|---|
| Nginx 部署后无样式 | build 时 base: '/',却访问 /docs/ | yarn build(VP_BASE=/docs/)后重新部署 |
| 只改 html 文件夹名 | 静态资源路径已写入 HTML/JS | 必须按新路径 VP_BASE=/新名/ yarn build |
想用相对路径 base: './' | VitePress 官方不支持(多 HTML 页面,非 Vue SPA) | 用 VP_BASE 环境变量打包时指定 |
| 与 Docusaurus 对比 | Docusaurus 同样不支持任意文件夹零 rebuild | 均需在 build 时指定 baseUrl / VP_BASE |
首页与目录
| 能力 | Docusaurus | VitePress(本项目做法) |
|---|---|---|
| 分类自动卡片索引 | ✅ /docs/category/xxx 自动生成 | ❌ 需手写 index.md + .doc-card |
| 侧栏分组折叠 | ✅ _category_.json | ✅ sidebar 分组 + collapsed |
| 卡片链接子路径 | 自动 | 首页 HTML 链接用 ./guide/xxx 相对路径 |
在线编辑(可选)
| 需求 | 推荐 |
|---|---|
| 在线写 md + 文件落盘 | ColonyNote(-r ./docs) |
| VitePress 完整文档站预览 | yarn dev |
| 语雀式知识库 + 数据库 | MrDoc(非 md 文件驱动,不适合与本项目共用) |
常用命令
| 操作 | 命令 |
|---|---|
| 本地开发 | yarn dev |
构建(默认 /docs/) | yarn build |
| 构建(网站根路径) | yarn build:root |
| 构建(自定义子路径) | cross-env VP_BASE=/xxx/ yarn vitepress build docs |
| 预览构建结果 | yarn serve |
常见问题
搜索按钮不显示
→ 确认 vitepress ≥ 1.0 正式版,search 写在 themeConfig 下。
侧栏和正文中间有大空白
→ 宽屏需同时覆盖 VPSidebar、VPContent、顶栏的 padding-left(见第四步 CSS)。
页面 404
→ 检查 sidebar 的 link 是否与 .md 文件名一致。
Nginx 部署后没有样式
→ VP_BASE 必须与访问子路径一致,重新 build 并拷贝 dist 内容。
只改了 html 下文件夹名,样式没了
→ 必须按新路径重新 VP_BASE=/新名字/ yarn vitepress build docs,不能只改文件夹。
构建后找不到 dist
→ 输出在 docs/.vitepress/dist/,不是项目根目录。
首页卡片点击 404
→ 检查 index.md 中链接是否为 ./guide/文件名(与 .md 文件名一致)。
新增文档后首页没有入口
→ 需同时更新 config.ts 侧栏和 index.md 卡片。
