背景

本博客使用的是 Hexo + Butterfly 主题。最初安装主题时,是直接把 butterfly 的源码克隆到 themes/butterfly/ 目录下,然后整体纳入到自己的 git 仓库进行管理。

这种方式有一个很大的问题:主题的数百个文件全部混入了自己的仓库,每次想升级主题时,要么手动 rsync + diff 合并,要么面临覆盖自己修改的风险,非常麻烦。

Butterfly 官方文档推荐的更优方案是:

  • 通过 npm 安装主题,主题文件存放在 node_modules/ 中,不进入自己的 git 仓库
  • 将主题配置迁移到 hexo 根目录下的 _config.butterfly.yml 文件,与主题代码完全解耦

这样一来,未来升级主题就只需要一条命令 npm update hexo-theme-butterfly,配置文件完全不受影响,非常优雅。

本文记录了从旧的 git 方式迁移到 npm 方式,并同步将 Butterfly 从 5.3.5 升级到 5.5.4 的完整过程。


迁移步骤

第一步:备份当前状态

在开始任何操作之前,先确保当前所有改动已经提交到 git:

1
2
cd /home/tianlejin/myblog
git add -A && git commit -m "backup before butterfly upgrade"

由于整个博客已在 git 管理下,后续任何步骤出问题都可以通过 git checkout 一键回滚。


第二步:创建 _config.butterfly.yml

这是本次迁移的核心步骤。将主题目录下的 _config.yml 内容复制到 hexo 根目录(即 txm_blog/)下:

1
cp txm_blog/themes/butterfly/_config.yml txm_blog/_config.butterfly.yml

Hexo 会自动将主题自带的 _config.yml 与根目录下的 _config.butterfly.yml 进行合并,且 _config.butterfly.yml 的优先级更高。从此以后,只需编辑 _config.butterfly.yml 即可,主题目录的 _config.yml 不需要也不应该再修改了。

注意:不要删除主题目录下的 _config.yml,Hexo 的合并机制需要它作为默认值基础。


第三步:保存自定义文件

如果在主题目录下有自定义的 layout 或 css 文件,切记先备份。本博客在主题目录中有两个自定义文件:

  • themes/butterfly/layout/_custom/photography.pug:相册页面自定义模板
  • themes/butterfly/source/css/_custom/custom.styl:自定义样式
1
2
cp -r txm_blog/themes/butterfly/layout/_custom /tmp/butterfly_custom_layout
cp -r txm_blog/themes/butterfly/source/css/_custom /tmp/butterfly_custom_css

第四步:删除旧的主题目录

1
rm -rf txm_blog/themes/butterfly

将旧主题目录整体删除,git 会记录这次删除操作。


第五步:通过 npm 安装最新版 Butterfly

在 hexo 根目录下执行:

1
2
cd txm_blog
npm install hexo-theme-butterfly

安装完成后,主题文件位于 txm_blog/node_modules/hexo-theme-butterfly/。由于 node_modules/ 已在 .gitignore 中,这些文件不会进入你的 git 仓库,仓库因此变得非常干净。

可以通过以下命令确认安装的版本:

1
cat node_modules/hexo-theme-butterfly/package.json | grep '"version"'

第六步:还原自定义文件

npm 安装方式下,不能直接修改 node_modules/ 里的文件(升级时会被覆盖)。自定义的 layout 和 css 文件需要放到 hexo 主目录下进行管理。

对于 layout 文件,创建对应目录并还原:

1
2
mkdir -p txm_blog/layout/_custom
cp /tmp/butterfly_custom_layout/photography.pug txm_blog/layout/_custom/

对于自定义样式文件,放到 source/css/ 目录下:

1
2
mkdir -p txm_blog/source/css/_custom
cp /tmp/butterfly_custom_css/custom.styl txm_blog/source/css/_custom/

需要确认 _config.butterfly.yml 中的 inject 配置或相关引用路径指向正确。


第七步:对比新旧配置,补充新增项

主题从 5.3.5 升级到 5.5.4 期间,新增了若干配置项。用 diff 命令查看新版默认配置与你的 _config.butterfly.yml 之间的差异:

1
2
diff txm_blog/node_modules/hexo-theme-butterfly/_config.yml \
txm_blog/_config.butterfly.yml

将新版中有而你的配置里没有的项手动添加到 _config.butterfly.yml 中。5.3.5 到 5.5.4 之间主要新增的配置项包括:

  • nav.display_post_title:导航栏是否显示当前文章标题(5.4.0 新增)
  • google_tag_manager:Google Tag Manager 支持,含 tag_iddomain 字段(5.4.0 新增)
  • mermaid.open_in_new_tab:在新标签页中打开 Mermaid 图表(5.5.4 新增)
  • mermaid.zoom_pan:Mermaid 图表的缩放和平移支持(5.5.4 新增)
  • structured_data:从布尔值 true 改为对象格式,含 enablealternate_name 字段(5.5.4 新增)
  • lazyload.native:是否使用浏览器原生懒加载代替 vanilla-lazyload(5.5.4 新增)

除了新增项,还有一个格式发生了变化的配置项,如果沿用旧格式会静默失效:

footer.copyright 在 5.4.0 中从布尔值改为了对象格式。旧格式 copyright: true 会导致底部的 Hexo 和 Butterfly 版本信息不显示,需要改为:

1
2
3
4
footer:
copyright:
enable: true
version: true # 是否在版权信息中显示 Hexo 和 Butterfly 的版本号

第八步:测试生成

1
2
cd txm_blog
hexo clean && hexo generate

检查是否有报错,并访问博客确认页面显示正常,重点检查:

  • 首页、文章页布局是否正常
  • 相册页(使用了自定义 photography.pug)是否正常渲染
  • 侧边栏、目录、评论系统是否正常

第九步:提交到 git

确认无误后,提交本次改动:

1
2
cd /home/tianlejin/myblog
git add -A && git commit -m "upgrade butterfly theme to 5.5.4, migrate to npm install"

可以看到,这次提交会删除大量主题文件(themes/butterfly/ 下几百个文件),同时新增 _config.butterfly.ymllayout/_custom/,仓库整体体积大幅缩减。


新版 butterfly 关于 mermaid的 疑似 bug?

升级完成后,使用过程中发现了一个 Butterfly 5.5.4 的 bug,在此记录修复过程。

问题现象:启用 mermaid(mermaid.enable: true)后,博客所有页面底部会出现大段可见的 JS 代码,像乱码一样堆在页脚下方。

根本原因:这是 Butterfly 5.5.4 的 bug,和 Hexo 版本无关,升级 Hexo 也无法解决。新版 mermaid.pug 模板新增了 openSvgInNewTab 函数,该函数里有一段 JS 模板字符串包含字面量 </body></html>,而 Hexo 的 body_end 注入器使用简单字符串匹配查找 </body>,因此注入点落在了 JS 模板字符串内部,导致其后所有 JS 代码以明文渲染到页面上。

需要特别注意的是,将 mermaid.open_in_new_tab 设为 false 并不能解决问题,因为该配置只控制是否显示”在新标签页打开”按钮,而包含 </body>openSvgInNewTab 函数体无论该配置如何设置,都会被完整生成到 HTML 中。

修复方法:直接修改 node_modules/hexo-theme-butterfly/layout/includes/third-party/math/mermaid.pug,将模板字符串中的 </body></html> 拆成字符串拼接,使其不再作为字面量出现:

1
2
3
4
5
// 修改前(有 bug)
const htmlSource = `...${svgSource}</body></html>`

// 修改后
const htmlSource = `...${svgSource}` + '<\/body><\/html>'

由于 node_modules 不进入 git,每次 npm install 后都会丢失这个改动。为此在 patches/postinstall-patch.sh 中保存了补丁脚本,并在 package.jsonpostinstall 钩子中自动执行,确保每次安装依赖后补丁都会被重新应用:

1
2
3
4
5
{
"scripts": {
"postinstall": "bash patches/postinstall-patch.sh"
}
}

未来升级

完成本次迁移后,以后升级 Butterfly 只需两步:

1
2
3
4
5
6
7
8
9
cd txm_blog
# 升级主题
npm update hexo-theme-butterfly

# 检查是否有新增配置项
diff node_modules/hexo-theme-butterfly/_config.yml _config.butterfly.yml

# 重新生成
hexo clean && hexo generate

配置文件完全独立,自定义文件也不在 node_modules/ 里,升级过程不会影响任何自定义内容。


参考资料