博客重启:从 Hexo 到 VitePress 的迁移之旅
背景
时隔7年,重新发现了自己在 GitHub Pages 上的技术博客(最后更新于 2018-08-21)。然而发现原始仓库只保留了编译后的静态 HTML 文件,Hexo 源代码已经遗失。面对这种情况,决定借此机会在 AI 的帮助下将博客迁移到更现代化的技术栈。
技术选型
为什么选择 VitePress?
选择了 VitePress 1.6.4 作为新的静态站点生成器的主要原因:
- 现代化技术栈:基于 Vue 3 + Vite,构建速度快,开发体验好
- 简洁高效:配置简单,开箱即用
- Markdown 友好:原生支持 Markdown,适合技术写作
- 主题可定制:可以轻松扩展默认主题
- 活跃维护:VitePress 是 Vue 团队维护的项目,生态活跃
对比旧的 Hexo 3.x:
- Hexo 基于 Node.js 但构建较慢
- VitePress 使用 Vite 构建,速度更快
- VitePress 对 TypeScript 和现代化工具链支持更好
迁移过程
1. 内容提取与转换
挑战:只有编译后的 HTML 文件,需要还原为 Markdown 格式
解决方案:
python
# 使用 BeautifulSoup 解析 HTML 并提取内容
from bs4 import BeautifulSoup
# 提取文章元数据
title = soup.find('h1', class_='post-title').text.strip()
date = soup.find('time', class_='post-time')['datetime'][:10]
tags = [a.text for a in soup.select('.post-tags a')]
# 提取文章正文
content = soup.find('div', class_='post-body')成果:成功提取了 33 篇文章及 31 张图片
2. VitePress 项目搭建
初始化项目
bash
npm init
npm add -D vitepress核心配置文件:.vitepress/config.mjs
javascript
export default {
title: '小码奔腾',
description: '测试开发、自动化、CI/CD',
lang: 'zh-CN',
cleanUrls: true,
themeConfig: {
nav: [
{ text: '首页', link: '/' },
{ text: '归档', link: '/archives' },
{ text: '标签', link: '/tags' },
{ text: '关于', link: '/about' }
],
search: {
provider: 'local',
options: {
locales: {
root: {
translations: {
button: { buttonText: '搜索文档' },
modal: { noResultsText: '无法找到相关结果' }
}
}
}
}
}
},
// 处理开发环境中的死链
ignoreDeadLinks: [
/^https?:\/\/localhost/,
'favicon.ico'
]
}3. 内容格式修复
迁移过程中遇到并解决了多个格式问题:
问题1:YAML Frontmatter 解析错误
yaml
# 错误:描述中包含冒号但未引号包裹
description: Error: spawn Xvfb ENOENT
# 修复:添加引号
description: "Error: spawn Xvfb ENOENT"解决脚本:自动为所有 description 字段添加引号
问题2:代码块中的行号干扰
markdown
# 问题:从 HTML 提取时带入了行号
1 public class Example {
2 // code
3 }
# 修复:移除行号
public class Example {
// code
}问题3:文章元数据展示
在每篇文章中添加了元数据展示区块:
html
<div class="article-metadata">
<span class="article-date">📅 2025-11-11</span>
<span class="article-tags">
🏷️ <a href="/tags#VitePress">VitePress</a>
</span>
</div>配合自定义 CSS 样式:
css
.article-metadata {
margin: 1.5rem 0;
padding: 1rem;
background: var(--vp-c-bg-soft);
border-radius: 8px;
}4. 自动化部署配置
Git 分支策略
- vitepress-source:存放 VitePress 源代码
- master:存放编译后的静态文件(GitHub Pages 部署分支)
GitHub Actions 工作流
创建 .github/workflows/deploy.yml:
yaml
name: Deploy VitePress to GitHub Pages
on:
push:
branches: [vitepress-source]
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm run docs:build
- uses: actions/upload-pages-artifact@v3
with:
path: .vitepress/dist
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/deploy-pages@v4配置要点
GitHub Pages 设置:
- 进入仓库 Settings → Pages
- Source 选择 "GitHub Actions"
权限配置:
yamlpermissions: contents: read pages: write id-token: write避免的坑:
- ❌ 不要在 deploy job 中添加
environment: github-pages - ✅ 直接使用
actions/deploy-pages@v4即可 - 原因:environment 配置会触发保护规则,导致部署失败
- ❌ 不要在 deploy job 中添加
5. 辅助工具
归档和标签页生成器:generate_archives.py
自动生成归档页面和标签页面的 Python 脚本:
python
def collect_articles(root_dir):
"""Collect all markdown articles with metadata"""
articles = []
for year_dir in ['2016', '2017', '2018', '2025']:
year_path = os.path.join(root_dir, year_dir)
if not os.path.exists(year_path):
continue
for root, dirs, files in os.walk(year_path):
for file in files:
if file.endswith('.md'):
file_path = os.path.join(root, file)
metadata = parse_frontmatter(file_path)
if metadata:
articles.append(metadata)
return sorted(articles, key=lambda x: x['date'], reverse=True)每次新增文章后运行此脚本,自动更新归档和标签索引。
最终成果
项目结构
python012.github.io/
├── .github/
│ └── workflows/
│ └── deploy.yml # GitHub Actions 部署配置
├── .vitepress/
│ ├── config.mjs # VitePress 主配置
│ ├── theme/
│ │ ├── index.js # 主题扩展
│ │ ├── custom.css # 自定义样式
│ │ └── components/ # 自定义组件
│ └── dist/ # 构建输出(自动生成)
├── public/
│ └── images/ # 图片资源
├── 2016/, 2017/, 2018/, 2025/ # 按年份组织的文章
├── archives.md # 归档页面
├── tags.md # 标签页面
├── about.md # 关于页面
├── generate_archives.py # 归档生成脚本
└── package.json # 项目依赖统计数据
- ✅ 迁移文章:33 篇
- ✅ 迁移图片:31 张
- ✅ 时间跨度:2016-2018
- ✅ 标签数量:24 个
- ✅ 自动部署:完全自动化
在线访问
- 📝 博客地址:https://python012.github.io/
后续发布新文章的步骤
快速指南(5步完成)
1. 创建文章文件
bash
# 按日期创建目录和文章
mkdir -p 2025/11/12
code 2025/11/12/my-new-article.md2. 编写文章内容
markdown
---
title: "文章标题"
date: "2025-11-12"
description: "文章描述"
tags: ["标签1", "标签2"]
---
<div class="article-metadata">
<span class="article-date">📅 2025-11-12</span>
<span class="article-tags">
🏷️ <a href="/tags#标签1">标签1</a> /
<a href="/tags#标签2">标签2</a>
</span>
</div>
# 文章标题
正文内容...3. 更新归档和标签索引
bash
python generate_archives.py4. 本地预览(可选)
bash
npm run docs:dev
# 访问 http://localhost:5173/5. 提交并推送
bash
git add .
git commit -m "新增文章:文章标题"
git push origin vitepress-source自动部署:推送后,GitHub Actions 会自动构建并部署,约 2-3 分钟后即可在线访问。
图片处理
如果文章包含图片:
bash
# 将图片放入 public/images/ 目录
cp my-image.png public/images/
# 在 Markdown 中引用
本地开发命令
bash
# 安装依赖(首次或更新依赖时)
npm install
# 启动开发服务器
npm run docs:dev
# 构建生产版本
npm run docs:build
# 预览构建结果
npm run docs:preview技术栈总结
核心技术
- VitePress 1.6.4:静态站点生成器
- Node.js 20:运行环境
- Vue 3:VitePress 基础框架
- Vite:构建工具
部署技术
- GitHub Actions:CI/CD 自动化
- GitHub Pages:静态站点托管
- Git:版本控制
开发工具
- Python 3:内容迁移脚本
- BeautifulSoup:HTML 解析
- Markdown:内容格式
经验总结
成功经验
- 选择现代化工具链:VitePress 的开发体验和构建速度远超旧的静态站点生成器
- 自动化优先:GitHub Actions 实现了推送即部署,无需手动构建
- 结构化组织:按年/月/日组织文章,清晰易维护
- 保留原始数据:成功恢复了所有文章的发布日期和标签
踩过的坑
Environment 配置导致部署失败
- 问题:在 workflow 中配置了
environment: github-pages - 影响:触发了环境保护规则,阻止部署
- 解决:移除 environment 配置,直接使用 deploy-pages action
- 问题:在 workflow 中配置了
死链检查过于严格
- 问题:开发环境中的 localhost 链接导致构建失败
- 解决:配置
ignoreDeadLinks忽略特定链接
YAML 解析错误
- 问题:Frontmatter 中的冒号需要引号包裹
- 解决:编写脚本批量修复所有文章
未来优化方向
- 评论系统:集成 Giscus 或类似评论系统
- 访问统计:添加 Google Analytics 或其他分析工具
- 搜索优化:改进本地搜索功能
- RSS 订阅:生成 RSS feed
- 自定义域名:配置自定义域名(如果需要)
相关链接:
- VitePress 官方文档:https://vitepress.dev/
- GitHub Actions 文档:https://docs.github.com/en/actions
本次博客迁移在 GitHub Copilot (Claude Sonnet 4.5) 协助下完成。