这篇文章用于记录本站从 NeXT 主题迁移到 Butterfly 主题的过程,也顺便记录这次由 Codex 辅助完成的整理,排查,配置和验证工作。

这不是一篇从零开始的 Hexo 建站教程,而是一篇迁移复盘。对我来说,这次迁移最重要的目标不是把站点一次性改得多花哨,而是保证已有文章,图片,公式和页面结构都能在新主题下继续正常工作。

迁移目标

最开始的需求很明确:把当前使用的 NeXT 主题换成 jerryc127/hexo-theme-butterfly,同时尽量降低风险。

具体约束主要有几条:

  • 已有博客内容必须继续正常渲染。
  • 已有文章图片必须继续正常显示。
  • 公式文章必须继续渲染正确,尤其不能被 Markdown 渲染器提前破坏。
  • 优先使用 Butterfly 官方推荐方案。
  • 移除 NeXT 主题和它的自定义残留。
  • 使用 npm 安装主题,不再把主题 vendored 到 themes/ 下。
  • 不迁移 NeXT 时代的外观自定义细节,先保证内容正常。

所以这次迁移不是简单把 _config.yml 里的 themenext 改成 butterfly,而是围绕内容兼容性做了一轮完整检查。

版本选择

这次选择了 hexo-theme-butterfly@5.5.5

Hexo 本身仍然保持在 6.3.0,没有顺手升级到新的大版本。原因很朴素:当前 Hexo 版本和站点插件已经可以正常配合 Butterfly 工作,而 Hexo 跨大版本升级可能牵动插件兼容性,对这次迁移目标没有直接收益。

最终依赖策略是:

  • 新增 hexo-theme-butterfly
  • 新增 Butterfly 需要的 hexo-renderer-pughexo-renderer-stylus
  • hexo-renderer-markdown-it-plus 替换默认的 hexo-renderer-marked
  • 保留现有稳定工作的 Hexo 主版本。
  • 后续依赖升级不为升级而升级,只在有明确收益或安全修复时单独评估。

这个选择让主题迁移和框架升级解耦,排查范围也更可控。

主题迁移

主题安装改为 npm 方式,主题源码位于 node_modules/hexo-theme-butterfly/,不再放进 themes/ 目录。

站点配置中,_config.yml 负责 Hexo 本身的配置,例如站点地址,permalink,deploy 和 generator。Butterfly 主题自己的配置则放在 _config.butterfly.yml。这个文件是从主题默认配置复制出来的覆盖配置,Hexo 会自动合并它。

这次做的主题侧配置包括:

  • 顶部导航。
  • KaTeX 数学公式配置。
  • 本地搜索配置。
  • 首屏默认 banner。
  • 头像和 favicon。
  • 侧栏位置。
  • GitHub,知乎和邮箱社交链接。
  • 作者卡文案和公告。
  • 自定义 CSS 注入。

NeXT 相关残留也被清理掉了,包括旧的 themes/next/ 和 NeXT 专用的 source/_data/ 自定义样式文件。这些文件已经不再被 Butterfly 读取,留着只会增加误导。

图片处理

图片是这次迁移里最需要谨慎确认的部分之一。

本站开启了 post_asset_folder: true,文章图片放在同名资源文件夹里,例如 foo.md 对应 foo/image.png,文章中用相对路径引用。过去 NeXT 时代站点配置里有 marked.prependRootmarked.postAsset 相关配置,但这属于旧渲染链路的一部分。

迁移后图片主要依赖 hexo-asset-img。这个插件会在 Markdown 渲染之前通过 Hexo 的 before_post_render 阶段处理图片路径,把相对图片链接转换成 Hexo 的 asset 标签,最终输出站点根路径下的图片地址,例如 /post/foo/image.png

这意味着图片处理不依赖具体 Markdown 渲染器。因此,后面把 hexo-renderer-marked 换成 hexo-renderer-markdown-it-plus 时,图片链路没有被一起改坏。

迁移验证时重点检查了已有文章图片的输出路径和访问状态,确认图片仍能正常显示。

公式处理

公式问题是这次迁移里最关键的一处排查。

一开始如果继续使用 hexo-renderer-marked,数学公式中的 *_ 可能会先被当作 Markdown 强调语法处理。例如公式中的乘号附近内容会被错误渲染成 <em> 标签。一旦 HTML 已经被破坏,主题侧再用 MathJax 或 KaTeX 都很难挽救。

最终采用 Butterfly 推荐路线:用支持数学语法的 hexo-renderer-markdown-it-plus 替换默认 marked 渲染器,并在 Butterfly 中配置 KaTeX。

当前方案是:

  • _config.butterfly.yml 中设置 math.use: katex
  • math.per_page: false,让主题按文章 front matter 决定是否加载公式资源。
  • 含公式文章使用 katex: true
  • 不再使用 mathjax: true
  • 不保留 _config.yml 中旧的 marked: 配置段。

验证结果是公式在构建时已经被服务端渲染为带 class="katex" 的 HTML,不再依赖浏览器端去修复一段已经损坏的公式文本。

视觉复刻

主题迁移完成后,又做了一轮很克制的视觉复刻。

这里没有尝试把 NeXT 的所有样式细节搬到 Butterfly。只恢复了几个对站点识别度比较重要的元素:

  • 使用原来的雪湖头像。
  • 恢复 favicon 和 apple touch icon。
  • 恢复顶部 banner 图。
  • 侧栏在宽屏下左置。
  • 恢复作者卡描述。
  • 恢复 GitHub,知乎和邮箱链接。
  • 关闭头像和社交图标 hover 时的旋转效果。

头像和 favicon 的原始资源当时并不在源码仓里,后来是从 .deploy_git/uploads/ 中找回的。.deploy_git/ 虽然是生成产物,平时不应该提交或手工编辑,但在这种源码仓缺失旧线上资源的情况下,它正好提供了一份可恢复的历史副本。

自定义样式没有直接改主题源码,而是写在 source/css/custom.css,再通过 Butterfly 的 inject.head 注入。其中有一个细节是 Butterfly 的部分 hover 样式选择器权重很高,覆盖时需要使用同等或更高权重的选择器,必要时加 !important

自动部署

迁移上线之后,又补上了自动部署流程。

现在源码仓 SnowyLake/BlogSource 每次 push 到 main 后,GitHub Actions 会自动运行:

1
2
3
4
npm ci
npm run clean
npm run build
npm run deploy

npm run deploy 仍然复用 Hexo 原有的 hexo deploy,它会把生成的 public/ 推送到 SnowyLake/snowylake.github.iomain 分支。这样对原有部署方式改动最小,只是把本地手动执行变成了云端自动执行。

为了让 Actions 能推送 Pages 仓库,这次没有复用本机常用 SSH key,而是单独创建了一把 deploy key:

  • 公钥添加到 SnowyLake/snowylake.github.io 的 Deploy keys,并允许写入。
  • 私钥保存到 SnowyLake/BlogSource 的 Actions secret HEXO_DEPLOY_KEY
  • 本地临时私钥文件在配置完成后删除。

后来 GitHub Actions 出现过 Node.js 20 is deprecated 的 warning。排查后确认,这个 warning 来自 GitHub JavaScript action 自身运行时,不是 Hexo 构建使用的 Node 版本。因此最终升级的是 workflow 中的 action 版本:

  • actions/checkout@v7
  • actions/setup-node@v6
  • webfactory/ssh-agent@v0.10.0

升级后自动部署重新跑通,Node 20 action runtime warning 消失。npm 依赖链里仍有一些 deprecated package 提示,但它们不阻塞构建和部署,可以以后单独评估。

最后的状态

这次迁移完成后,站点处于一个更清晰的状态:

  • Hexo 仍保持稳定的 6.3.0
  • 主题切换为 npm 安装的 Butterfly。
  • 主题配置集中在 _config.butterfly.yml
  • 公式渲染改为 KaTeX + hexo-renderer-markdown-it-plus
  • 图片继续通过 hexo-asset-img 和文章资源文件夹处理。
  • NeXT 主题和对应残留已经移除。
  • 自动部署由 GitHub Actions 接管。

还有一些后续可以慢慢做的事,例如建立文章封面系统,给页面配置专属 banner,补 RSS feed,或者把旧的建站记录补上一段历史说明。

但这次迁移本身的核心目标已经完成:不追求一次性重做整个站点,先让已有内容在新主题下稳定,正确,可维护地继续存在。