基于 Hexo 8 + NexT 8 的静态博客全新迁移过程记录
早在很多年前,我基于 Hexo + NexT 搭建了这个静态博客。它经历过多次小修小补,也在 2020 年做过一次比较完整的升级。几年之后再回头看,Hexo、Node.js、NexT 的版本体系和推荐安装方式都已经发生了明显变化:旧项目还能作为资料库存在,但继续在旧目录里原地升级,风险会越来越高。
所以这次采用了一个更稳妥的方案:不在旧项目上原地升级,而是创建一个全新的 Hexo 8 + NexT 8 项目,再把仍然有效的配置、文章和资源选择性迁移过来。这样做的好处是,最终得到的是一个干净、可维护、可复现的新项目,而不是一个背着多年历史包袱的“能跑就行”的项目。
本文记录完整迁移过程,包含方案选择、环境准备、项目初始化、配置迁移、主题迁移、内容迁移、构建验证、踩坑处理和后续建议。
最终效果与迁移结果
本次迁移完成后,新博客具备如下特征:
- 使用
hexo@8.1.2和hexo-theme-next@8.27.0 - 使用 npm 安装 NexT 主题,而不是复制旧主题源码
- 使用根目录
_config.next.yml管理 NexT 8 的主题配置 - 保留旧博客的标题、作者、语言、域名、永久链接格式、头像、菜单和文章资源目录策略
- 迁移全部旧文章、页面和资源
- 删除或关闭旧的第三方统计、搜索、评论、QQ 404 等非必要能力
- 迁移后继续完成一轮偏审美和阅读体验的配置调优:Gemini 布局、跟随系统深色模式、关闭页面入场动画、优化 TOC、字体栈和代码块主题
- 本地构建验证通过:初始迁移生成
317 files,补写迁移复盘和完成配置调优后最新生成321 files - 未执行线上部署、远程推送或任何需要凭证的外部操作
迁移后的内容规模如下:
| 项目 | 数量 |
|---|---|
| 文章 Markdown | 42 |
| 非文章 Markdown 页面 | 4 |
source 文件 |
143 |
| 文章资源目录 | 28 |
其中初始迁移阶段是 41 篇旧文章、142 个 source 文件;补写本文后,文章数变为 42,source 文件数变为 143。旧 404 页面同时存在 Markdown 与 HTML 两份实现,并且都依赖外部 QQ/qzone 脚本,本次迁移将它重写成根级 source/404.md,生成 public/404.html,因此旧内容本身仍比原项目更干净,也更符合静态托管平台的 404 约定。
本次迁移方案
本次采用的迁移方案如下:
- 先只读盘点旧项目,记录配置、依赖和内容数量
- 在空的新目录中初始化全新 Hexo 项目
- 锁定 Hexo / NexT 版本,提交
package-lock.json - 迁移站点级配置,例如标题、作者、语言、URL、永久链接和文章资源目录策略
- 按 NexT 8 的配置格式重新编写
_config.next.yml - 迁移文章、页面、头像、favicon、CNAME 和文章资源目录
- 替换旧 404 页面中的第三方外部脚本
- 运行本地生成命令,发现问题后修复
- 编写迁移文档,记录命令、配置取舍、内容清单和验证结果
- 用 git 分阶段记录每个有意义的迁移步骤
这个方案比“直接复制旧项目然后升级依赖”更慢一点,但好处非常明显:
- 依赖更干净
- 配置更可读
- 主题升级路径更正规
- 迁移历史可审计
- 后续维护成本更低
环境与版本准备
迁移时的本地环境如下:
1 | node -v |
实际使用版本:
1 | node v24.15.0 |
Hexo 8 对 Node.js 有最低版本要求,本机 Node 版本满足要求。迁移前还查询了当前 Hexo 与 NexT 的 npm 版本:
1 | npm view hexo version engines --json |
最终锁定的关键版本如下:
| 组件 | 版本 |
|---|---|
| Hexo | 8.1.2 |
| Hexo CLI | 4.3.2 |
| NexT | 8.27.0 |
| Node.js 要求 | >=20.19.0 |
这里没有直接使用一个永远漂移的 latest 状态,而是把最终安装结果写入 package-lock.json。以后如果要升级,可以从一个明确的版本点出发。
旧项目基线盘点
旧项目路径:
1 | cd /home/lml/projects/bitkylin-blog-old |
首先确认旧项目工作区状态:
1 | git status --short --branch |
然后盘点内容规模:
1 | find source/_posts -maxdepth 1 -type f -name '*.md' | wc -l |
盘点结果:
| 项目 | 旧项目数量 |
|---|---|
| 文章 Markdown | 41 |
| 非文章 Markdown 页面 | 4 |
source 文件 |
143 |
| 文章资源目录 | 28 |
旧 _config.yml 中需要保留的站点身份主要包括:
1 | title: 比特麒麟 |
这些字段属于博客的“身份信息”,如果随意改变,会影响旧链接、文章资源路径和站点识别度,所以应当保留。
初始化全新 Hexo 项目
新项目路径:
1 | cd /home/lml/projects/bitkylin-blog |
初始化脚手架:
1 | npx hexo-cli@4.3.2 init . |
初始化后检查 Hexo 是否可用:
1 | npx hexo version |
最终依赖中保留了 Hexo 基础生成器、渲染器、server 和 NexT 主题:
1 | hexo@8.1.2 |
这里做了几个重要取舍:
- 移除了 starter 自带的
hexo-theme-landscape - 不安装旧项目中的部署插件
- 不保留任何部署 npm script
- 增加
.nvmrc和package.json的engines.node - 把
public/、node_modules/、db.json、.deploy_git/等生成产物加入.gitignore
迁移站点级配置
新项目的 _config.yml 不是从旧文件整段复制,而是以 Hexo 8 的默认配置为基础,只迁移仍然有效且必要的字段。
核心配置初始迁移时保留了旧博客身份,后续又按当前审美做了一次轻量整理。当前关键配置如下:
1 | title: 比特麒麟 |
这里的变化不是为了 SEO 生硬堆词,而是把副标题、描述和关键词改成更符合个人气质与真实技术栈的表达。subtitle 使用“靡不有初,鲜克有终”保留中文长期主义气质;description 保留英文句子本身,不再把中英文格言混在一行;keywords 则体现 Java、Linux、Docker、Spring Boot 以及部分前端技术栈。
其中 post_asset_folder: true 很关键。旧文章中有大量与文章同名的资源目录,关闭它会让图片迁移变得麻烦,也容易出现资源路径错位。
部署配置没有迁移到 active 配置中。原因很简单:本次迁移的目标是本地完成项目升级和内容迁移,不执行线上发布。部署仓库和分支属于上线阶段的事情,应当单独规划,不能在迁移阶段混入外部副作用。
迁移 NexT 主题配置
旧项目里有一份 NexT 主题配置备份:
1 | tools/next主题备份/_config.yml |
但旧配置不能直接复制到新项目中。NexT 8 推荐使用 npm 安装主题,并通过根目录 _config.next.yml 管理自定义配置。因此本次新建了:
1 | _config.next.yml |
保留的主题个性化包括:
| 能力 | 决策 |
|---|---|
Gemini 布局 |
从初始 Pisces 调整为 Gemini,整体更现代 |
| 深色模式 | 使用 darkmode: true,不使用 lightdark 路径 |
| 页面入场动画 | motion.enable: false,避免加载时拖沓 |
| 菜单 | 保留 home / about / tags / categories / archives |
| 侧边栏 | 保留左侧栏 |
| 头像 | 保留 /uploads/avatar.jpg,并启用圆角 |
| 社交链接 | 保留 GitHub、Email、简书、知乎、个人资料 |
| TOC | 保留,不显示编号,长标题换行,最大深度 4 |
| 代码块 | 使用 Atom One 明暗主题、mac 风格复制按钮、显示语言名 |
| 字体 | 使用本地系统字体栈和现代代码字体栈,不依赖外部 CDN |
| 返回顶部 | 保留 |
| 阅读进度条 | 保留 |
| GitHub banner | 保留 |
| Fancybox | 开启图片预览,保留 mediumzoom 关闭 |
| Creative Commons | 保留 by-nc-sa |
保持关闭或舍弃的能力包括:
| 能力 | 决策 | 原因 |
|---|---|---|
| busuanzi 统计 | 关闭 | 旧第三方统计,本次不恢复 |
| 本地搜索 | 关闭 | 需要额外 generator,旧项目也未启用 |
| 评论系统 | 不配置 | 旧配置多为占位或关闭 |
| 打赏、日历、聊天等 | 不配置 | 非本次迁移目标 |
| 旧主题源码 | 不复制 | 避免未来升级困难 |
这一步的核心原则是:保留博客的味道,不继承旧配置债务。
迁移后一轮页面风格调优
初始迁移完成后,我又根据实际预览效果做了一轮更偏审美和阅读体验的配置调整。这一轮调整并不是恢复旧主题的全部细节,而是围绕“现代、克制、清爽、适合技术长文阅读”的方向收敛配置。
当前 _config.next.yml 中最关键的变化如下:
1 | scheme: Gemini |
这里有几个值得记录的取舍:
- 页面加载动画关闭,但页脚爱心跳动保留。前者会影响进入页面的节奏,后者只是一个轻量点缀。
- 深色模式走
darkmode: true,而不是lightdark.enable: true。实际排查时发现,lightdark可以让页面主体变暗,但代码块深色主题仍可能不切换;NexT 的代码高亮深色 CSS 与darkmode路径更匹配。 - 代码主题使用
atom-one-light/atom-one-dark,比默认主题更适合技术文章。 - TOC 最大深度从 6 收敛到 4,并允许长标题换行,避免侧边栏目录过碎。
- 先不写自定义 Stylus 样式。NexT 官方后续可能继续优化默认样式,过早自定义会增加升级冲突。
字体配置也单独整理了一次,原则是本地系统字体优先,不依赖 Google Fonts 或其它外部 CDN:
1 | font: |
全局字体覆盖 macOS、Ubuntu、Windows 和常见中文字体;代码字体优先使用 JetBrains Mono、Source Code Pro,并兼容本机安装的 Nerd Font 补丁字体。Nerd Fonts 只作为增强项,不作为公开博客的硬依赖,因为访客本机未安装时浏览器会自然 fallback。
迁移文章、页面和资源
内容迁移主要执行如下操作:
1 | cp -a /home/lml/projects/bitkylin-blog-old/source/_posts/. source/_posts/ |
这里保留了:
- 全部 41 篇文章
about、categories、tags页面- 头像文件
- favicon
- CNAME 域名身份文件
- 28 个文章资源目录
旧 404 页面没有原样复制。它依赖 QQ/qzone 外部脚本和样式,本次改写为一个根级 Markdown 页面,并通过 permalink: /404.html 生成静态托管平台通常识别的 public/404.html:
1 | --- |
这样做的好处是:
- 没有外部脚本依赖
- 不会引入旧第三方服务
- 页面语义更清楚
- 后续维护更简单
第一次构建验证
内容迁移完成后,执行:
1 | npx hexo clean |
本次比较幸运,完整生成一次通过,初始迁移阶段最终输出:
1 | 317 files generated |
后续补写迁移复盘文章、完成主题风格调优和字体配置后,再次执行生成,最新输出为:
1 | 321 files generated |
这说明旧文章的 front matter、Markdown、资源路径与 Hexo 8 / NexT 8 的组合基本兼容,后续主题配置调整也没有破坏构建。
如果以后迁移类似项目,构建失败时可以优先排查:
- front matter 中未转义的冒号或特殊字符
- 非法日期格式
- 文章资源目录与
post_asset_folder不一致 - 图片文件名大小写问题
- 旧主题标签或旧 HTML 混排语法
- 第三方脚本在新版渲染器下的兼容性
审计与安全检查
为了确认迁移结果不是“看起来能跑”,还做了几类审计。
检查是否安装部署插件:
1 | npm ls hexo-deployer-git |
结果为空,说明没有安装。
检查是否有 deploy npm script:
1 | node -e "const p=require('./package.json'); if (p.scripts?.deploy) throw new Error('deploy script exists'); console.log('no deploy script')" |
检查生成产物是否入库:
1 | git ls-files public |
这些命令都没有输出,说明生成产物没有被 git 跟踪。
检查 404 页面是否仍包含旧外部脚本:
1 | git grep -n -E "qzone|qq.com|search_children" -- source/404.md || true |
结果无输出。
本地还启动过 Hexo server 做路由冒烟验证,以下路径均返回 200:
1 | / |
Git 提交记录
本次迁移按阶段提交,方便后续回看和回滚:
| Commit | 说明 |
|---|---|
e597abe |
建立可复现的现代 Hexo 迁移基线 |
751e381 |
迁移站点身份以保留博客可识别性 |
71a2cee |
按 NexT v8 方式复刻兼容主题个性化 |
2960a0d |
迁移博客内容与资源并记录取舍 |
4878b44 |
消除旧文中的可执行部署命令示例 |
9f9b18a |
记录迁移过程以支持中文展示交付 |
d81cd5f |
记录 Hexo NexT 全新迁移复盘 |
1be5d2f |
沉淀博客审美偏好以稳定后续调优 |
我比较推荐这种迁移方式:每一步都可以独立解释,每个 commit 都有明确目的。迁移不是一把梭,而是可以复盘、可以验证、可以回滚。
和 2020 年升级方式的区别
2020 年那次升级,整体思路是“创建新脚手架 + 复制旧配置和主题 + 迁移 source”。这在当时是可行的,因为主题和脚手架变化没有现在这么大。
这次迁移做了几个变化:
- 不再复制 NexT 主题源码,而是使用 npm 包
- 不再机械复制主题配置,而是按 NexT 8 的配置项重新映射
- 不再保留部署插件和部署配置
- 不再恢复旧第三方统计、搜索、评论和 QQ 404
- 用
package-lock.json锁定版本 - 用中文迁移文档记录每个阶段的命令、数据变化和取舍
如果说 2020 年的升级重点是“让旧博客跟上新版本”,那么这次的重点是“把博客迁移到一个可长期维护的新基线”。
踩坑与经验
1. 不要直接复制旧主题配置
旧 NexT 配置文件很长,里面既有真正使用的配置,也有大量默认值、注释和第三方服务占位。直接复制的短期收益很高,但长期会留下大量不可解释的配置债务。
更好的做法是:
- 打开新版 NexT 默认配置
- 找出旧配置中真正启用的能力
- 只把仍然兼容且仍然想保留的 override 写入
_config.next.yml - 把舍弃项写进迁移文档
2. 文章资源目录要优先保护
旧文章中包含大量图片。如果 post_asset_folder 设置不正确,构建也许可以通过,但页面中的图片会出问题。因此这类配置不应随便改。
3. 404 页面要特别检查
很多旧博客会复制一些外部 404 模板,看起来漂亮,但几年后这些外部脚本可能已经不再稳定,也可能不符合当前的隐私和维护要求。迁移时最好改成本地静态页面。
4. 迁移文档不是额外负担
本次单独维护了一份迁移审计记录,记录版本、命令、commit、配置取舍、内容计数、功能变化和验证结果。它的价值不只在“交付好看”,更在于以后再次升级时能知道当时为什么这么做。
后续建议
本次迁移没有执行线上部署。如果后续要真正上线,建议单独做一个部署阶段,至少确认:
- GitHub Pages 仓库和分支策略
- CNAME 与 HTTPS 配置
- 是否恢复部署插件
- 是否需要 CI 自动构建
- 是否需要搜索、评论或统计功能
这些都不应该混在迁移阶段一次性完成。迁移阶段最重要的是先得到一个干净、稳定、可生成的新博客。
总结
这次迁移的核心体会是:静态博客升级不难,难的是克制。旧项目中有很多“当年能用”的配置和脚本,但它们未必适合继续保留。真正稳妥的迁移,不是把旧目录搬到新目录,而是重新判断每一项配置和功能是否仍然值得存在。
最终结果是,一个基于 Hexo 8 + NexT 8 的全新博客项目已经建立完成,旧文章和主要身份信息得到保留,旧第三方服务和部署副作用被移除,本地构建和路由验证均通过。对于一个多年未维护的静态博客来说,这样的迁移方式比原地升级更安全,也更适合长期维护。