背景
我的博客建于 2014 年,最初使用 WordPress 搭建。十年来,积累了 282 篇文章,涉及技术笔记、产品评测、行业观察等多个类别。
但随着时间推移,WordPress 的问题越来越明显:
- 性能瓶颈:每次访问都要查询数据库,响应慢,需要缓存插件维持
- 维护负担:核心、插件、主题需要频繁更新,安全隐患多
- 资源消耗:PHP + MySQL 的组合对服务器资源要求较高
- 备份复杂:需要同时备份数据库和文件,恢复流程繁琐
2026 年初,我决定将博客迁移到 Hugo —— 一个基于 Go 语言的静态网站生成器。
迁移完成后,博客的访问速度提升了 5-10 倍,服务器资源占用降低了 90% 以上。
本文记录了整个迁移过程,希望对有类似需求的朋友有所帮助。
一、为什么选择 Hugo?
在决定迁移之前,我对比了几种主流的静态网站生成器:
| 工具 | 优点 | 缺点 |
|---|---|---|
| Hugo | 极快(毫秒级构建)、Go 原生、主题丰富 | 模板语法需要学习 |
| Jekyll | GitHub 原生支持、生态成熟 | Ruby 依赖、构建较慢 |
| Hexo | 中文社区活跃、上手简单 | Node.js 依赖、插件质量参差 |
| Astro | 现代化、支持多框架 | 相对较新、迁移资源少 |
最终选择 Hugo 的原因:
- 构建速度:282 篇文章的构建时间不到 5 秒
- 单文件部署:Hugo 是一个独立二进制文件,无需安装依赖
- 主题生态:PaperMod、Stack 等主题现代且维护活跃
- 迁移工具:社区有成熟的 WordPress 转 Hugo 方案
二、迁移方案设计
2.1 迁移流程概览
WordPress (MySQL)
↓
导出 WXR XML(WordPress 导出工具)
↓
PHP 转换脚本(自定义 wp2hugo.php)
↓
Hugo Markdown 文件
↓
Hugo 构建
↓
静态 HTML(Nginx 托管)
2.2 关键决策
决策一:使用 PHP 转换而非现成工具
社区有现成的 WordPress to Hugo 导入工具(如 wordpress-to-hugo-exporter 插件),但我选择自己写 PHP 脚本,原因:
- 更灵活地处理中文标题和标签
- 可以自定义 front matter 格式
- 对数据有更精细的控制
决策二:保留原有 URL 结构
为了 SEO,迁移后的文章 URL 需要与原 WordPress 保持一致。WordPress 的 URL 格式是 /年份/月份/文章名/,Hugo 默认是 /posts/文件名/。
最终选择折中方案:按年份分目录,文件名使用 post slug。
决策三:文章内容保持 HTML
WordPress 文章内容是 HTML 格式。虽然有工具可以转换为 Markdown,但:
- 10 年积累的 HTML 格式复杂,转换容易出错
- Hugo 的 Goldmark 渲染器支持在 Markdown 中嵌入 HTML
- 保留 HTML 更安全,避免格式丢失
在 Hugo 配置中启用 HTML 支持:
[markup]
[markup.goldmark]
[markup.goldmark.renderer]
unsafe = true # 允许 HTML
三、迁移实施
3.1 导出 WordPress 数据
WordPress 后台 → 工具 → 导出 → 选择"所有内容",导出 WXR 格式的 XML 文件。
我的导出文件约 3MB,包含所有文章、页面、分类、标签和评论(评论未迁移)。
3.2 编写转换脚本
这是迁移的核心步骤。我写了一个 PHP 脚本 wp2hugo.php:
<?php
$xmlFile = '/path/to/wordpress-export.xml';
$outputDir = '/path/to/hugo/content/posts';
$xml = simplexml_load_file($xmlFile);
$namespaces = $xml->getNamespaces(true);
function yaml_escape($str) {
$str = (string)$str;
$str = str_replace('\\', '\\\\', $str);
$str = str_replace('"', '\\"', $str);
$str = str_replace("\n", '\\n', $str);
return '"' . $str . '"';
}
$count = 0;
foreach ($xml->channel->item as $item) {
$wp = $item->children($namespaces['wp']);
$post_type = (string)$wp->post_type;
$status = (string)$wp->status;
// 只处理已发布的文章
if ($post_type !== 'post' || $status !== 'publish') continue;
$title = trim((string)$item->title);
$pubDate = date('Y-m-d', strtotime((string)$item->pubDate));
$slug = (string)$wp->post_name;
$body = (string)$item->children($namespaces['content'])->encoded;
// 提取分类和标签
$categories = [];
$tags = [];
foreach ($item->category as $cat) {
$domain = (string)$cat->attributes()['domain'];
$name = trim((string)$cat);
if ($domain === 'category') $categories[] = $name;
elseif ($domain === 'post_tag') $tags[] = $name;
}
// 生成 Hugo front matter
$fm = "---\n";
$fm .= "title: " . yaml_escape($title) . "\n";
$fm .= "date: " . $pubDate . "T00:00:00+08:00\n";
$fm .= "draft: false\n";
if ($categories) {
$fm .= "categories:\n";
foreach ($categories as $c) $fm .= " - " . yaml_escape($c) . "\n";
}
if ($tags) {
$fm .= "tags:\n";
foreach ($tags as $t) $fm .= " - " . yaml_escape($t) . "\n";
}
$fm .= "---\n\n";
// 按年份分目录保存
$yearDir = $outputDir . '/' . substr($pubDate, 0, 4);
if (!is_dir($yearDir)) mkdir($yearDir, 0755, true);
$filename = $yearDir . '/' . preg_replace('/[^a-z0-9-]/i', '-', $slug) . '.md';
file_put_contents($filename, $fm . $body);
$count++;
}
echo "Converted: $count posts\n";
脚本要点:
- 使用 PHP 的 SimpleXML 解析 WXR 格式
- 正确处理 WordPress 命名空间(
wp:前缀) - 对中文标题进行 YAML 转义
- 按发布年份创建子目录
运行脚本:
php wp2hugo.php
# 输出: Converted: 282 posts
3.3 配置 Hugo
创建 config.toml:
baseURL = 'https://zhaoyanblog.com/'
languageCode = 'zh-cn'
defaultContentLanguage = 'zh-cn'
title = '赵岩的技术笔记'
theme = 'PaperMod'
enableRobotsTXT = true
[pagination]
pagerSize = 15
[params]
env = 'production'
description = '记录学习、工作和思考'
defaultTheme = 'auto'
ShowReadingTime = true
ShowCodeCopyButtons = true
ShowBreadCrumbs = true
[taxonomies]
category = 'categories'
tag = 'tags'
[outputs]
home = ['HTML', 'RSS', 'JSON']
[markup]
[markup.goldmark]
[markup.goldmark.renderer]
unsafe = true # 允许 HTML
[markup.highlight]
codeFences = true
guessSyntax = true
style = 'github'
3.4 安装主题
cd /path/to/hugo
git clone https://github.com/adityatelange/hugo-PaperMod themes/PaperMod --depth=1
3.5 构建和部署
创建 build.sh:
#!/bin/bash
cd /home/zhaoyan/zhaoyanblog/hugo
/usr/local/bin/hugo "$@"
cp public/index.json public/page/ 2>/dev/null
Nginx 配置:
server {
listen 443 ssl;
server_name zhaoyanblog.com;
ssl_certificate cert/zhaoyanblog.com.pem;
ssl_certificate_key cert/zhaoyanblog.com.key;
root /home/zhaoyan/zhaoyanblog/hugo/public;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
四、遇到的问题与解决
问题一:YAML 格式错误
WordPress 文章标题中包含引号、冒号等特殊字符,直接写入 YAML front matter 会导致解析错误。
解决方案:在 PHP 脚本中实现 yaml_escape() 函数,对特殊字符进行转义:
function yaml_escape($str) {
$str = str_replace('\\', '\\\\', $str);
$str = str_replace('"', '\\"', $str);
$str = str_replace("\n", '\\n', $str);
return '"' . $str . '"';
}
问题二:代码高亮失效
WordPress 使用 WP-Syntax 等插件实现代码高亮,迁移后代码块变成普通文本。
解决方案:Hugo 内置 Chroma 代码高亮,但需要调整配置:
[markup.highlight]
codeFences = true
guessSyntax = true # 自动检测语言
style = 'github'
对于旧的 <pre lang="php"> 格式,需要手动转换为 Markdown 代码块:
```php
// 代码内容
```
问题三:图片路径问题
WordPress 的图片存储在 /wp-content/uploads/ 目录,迁移后路径失效。
解决方案:
- 将 WordPress 的
wp-content/uploads/目录复制到 Hugo 的static/wp-content/uploads/ - 或者修改文章中的图片路径为新的静态资源路径
问题四:URL 重定向
WordPress 原有的 URL 结构(如 /2024/01/hello-world/)与 Hugo 不同,可能导致 SEO 损失。
解决方案:在 Nginx 中添加重定向规则,或使用 Hugo 的 aliases 功能:
---
title: "文章标题"
aliases:
- /2024/01/hello-world/
---
五、迁移效果
5.1 性能对比
| 指标 | WordPress | Hugo | 提升 |
|---|---|---|---|
| 首页加载时间 | 1.2s | 0.15s | 8x |
| 文章页加载时间 | 0.8s | 0.1s | 8x |
| 服务器内存占用 | 400MB+ | <50MB | 8x+ |
| 构建时间 | - | 4.8s | 282篇文章 |
5.2 文章统计
总文章数:282
年份分布:
2014: 146 篇
2015: 82 篇
2016: 21 篇
2017: 14 篇
2018: 8 篇
2019: 4 篇
2020: 3 篇
2025: 1 篇
2026: 3 篇(含本文)
5.3 维护简化
迁移前(WordPress):
- 每月更新 WordPress 核心
- 定期更新插件和主题
- 监控数据库性能
- 维护缓存插件
迁移后(Hugo):
- 写 Markdown →
hugo→ 部署 - 偶尔更新 Hugo 版本
- 无数据库维护
- 无缓存需求
六、总结与建议
6.1 迁移是否值得?
如果你满足以下条件,迁移是值得的:
- 文章数量在 1000 篇以内
- 对评论功能需求不高(或可使用第三方评论如 Giscus)
- 希望降低服务器成本
- 追求极致的访问速度
- 愿意投入时间处理迁移细节
如果你依赖以下 WordPress 特性,可能需要再考虑:
- 复杂的插件生态(会员、电商等)
- 大量用户互动(评论、社区)
- 多作者协作
- 可视化编辑器(Gutenberg)
6.2 迁移建议
1. 充分测试
在正式切换前,先在测试环境验证迁移结果。检查:
- 所有文章能否正常访问
- 图片、链接是否有效
- 搜索功能是否正常
- RSS 订阅是否可用
2. 保留回退能力
迁移完成后,保留 WordPress 的数据库和文件备份至少一个月。如果发现问题,可以快速回退。
3. 逐步优化
迁移后不要急于删除 WordPress。先稳定运行一段时间,再逐步:
- 清理无效的 HTML 标签
- 优化图片资源
- 更新文章内容
4. 利用 AI 辅助
本次迁移过程中,我使用了 OpenClaw(AI 助手)来:
- 分析 WordPress 导出数据结构
- 生成和调试 PHP 转换脚本
- 编写 Hugo 配置文件
- 排查部署问题
AI 可以显著加速迁移过程,尤其是处理重复性工作时。
七、后续计划
迁移完成不是终点,而是新的起点。后续计划:
- 主题定制:基于 PaperMod 进行个性化定制
- SEO 优化:完善 meta 标签、sitemap、结构化数据
- 评论系统:集成 Giscus(基于 GitHub Discussions)
- 搜索增强:利用 Hugo 的 JSON 输出实现全文搜索
参考资料
本文写于 2026 年 2 月 22 日。感谢 OpenClaw 在迁移过程中提供的协助。