Skip to content

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 DefaultTheme

4.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.tssidebar增加 { 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 docs

Linux / 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.cssflex-grow: 0 + order 调整
侧栏不贴左、中间大空白只改 --vp-sidebar-width,未同步覆盖宽屏居中逻辑同时覆盖 VPSidebarVPContent、顶栏 padding-left(见第四步 CSS)
侧栏 width 单独 !important与官方 ≥1440px 布局冲突侧栏只改边框/背景,宽度用 :root { --vp-sidebar-width }

内容与链接

问题原因处理
右侧「本页目录」重复同一页两个 ## 目录只保留一个,或改用列表不写 h2
侧栏页面 404link: '/guide/plc' 与文件名 yh-plc-points.md 不一致link 必须与 .md 文件名(无后缀)一致
HTML 标题 <h2 id="...">大纲解析不稳定优先用标准 Markdown ## 标题

部署

问题原因处理
Nginx 部署后无样式build 时 base: '/',却访问 /docs/yarn buildVP_BASE=/docs/)后重新部署
只改 html 文件夹名静态资源路径已写入 HTML/JS必须按新路径 VP_BASE=/新名/ yarn build
想用相对路径 base: './'VitePress 官方不支持(多 HTML 页面,非 Vue SPA)VP_BASE 环境变量打包时指定
与 Docusaurus 对比Docusaurus 同样不支持任意文件夹零 rebuild均需在 build 时指定 baseUrl / VP_BASE

首页与目录

能力DocusaurusVitePress(本项目做法)
分类自动卡片索引/docs/category/xxx 自动生成❌ 需手写 index.md + .doc-card
侧栏分组折叠_category_.jsonsidebar 分组 + 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 下。

侧栏和正文中间有大空白
→ 宽屏需同时覆盖 VPSidebarVPContent、顶栏的 padding-left(见第四步 CSS)。

页面 404
→ 检查 sidebarlink 是否与 .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 卡片。


参考

技术笔记文档