背景
原来的博客一直使用 Butterfly 主题,后面想额外保留一个 cactus 主题页面,用来展示更简洁的博客风格。
目标是:
- 主站继续使用 Butterfly;
- cactus 作为子站存在;
- 两个主题互不覆盖静态资源;
- 最终仍然只发布一个 GitHub Pages 站点。
最终访问结构如下:
1 | / Butterfly 主站 |
最终方案
不要让两个主题直接混在同一个输出目录里生成。
最终采用的方案是:
1 | cactus-theme/ cactus 独立构建输出 |
也就是说:
1 | 先生成 cactus -> cactus-theme/ |
这样两个主题在构建阶段是分开的,不会互相覆盖 main.js、index.css 这类同名文件。
配置文件
根目录 _config.yml 仍然保持 Butterfly 作为默认主题:
1 | theme: butterfly |
Butterfly 的主题配置继续放在:
1 | _config.butterfly.yml |
cactus 使用单独配置:
1 | theme: cactus |
这里最重要的是 public_dir 和 root 要对应。
public_dir: cactus-theme 表示 cactus 先输出到本地的 cactus-theme/ 文件夹。
root: /cactus-theme/ 表示部署后 cactus 的访问路径是 /cactus-theme/。
如果这两个不一致,页面可能能打开,但 CSS、JS、图片路径会错。
构建脚本
使用 PowerShell 脚本统一构建两个主题:
1 | $ErrorActionPreference = "Stop" |
完整脚本放在:
1 | tools/build-dual.ps1 |
构建流程是:
- 清理旧的
public/和cactus-theme/; - 使用
_config.yml,_config.cactus.yml生成 cactus; - 再普通执行
hexo generate生成 Butterfly 到public/; - 把 cactus 的输出复制到
public/cactus-theme/; - 最终只部署
public/。
package.json 命令
package.json 中保留快捷命令:
1 | "scripts": { |
以后本地构建:
1 | npm run build:dual |
构建并发布:
1 | npm run deploy:dual |
这里 package.json 和 PowerShell 脚本是配套关系,缺一不可。
package.json 负责提供统一入口:
1 | npm run build:dual |
这样平时不用记住一长串 Hexo 命令,也不需要手动执行复制目录的步骤。
真正的构建流程放在:
1 | tools/build-dual.ps1 |
PowerShell 脚本负责做具体事情:
- 清理旧的
public/和cactus-theme/; - 生成 cactus 到
cactus-theme/; - 再生成 Butterfly 到
public/; - 把
cactus-theme/复制进public/cactus-theme/; - 删除临时的
cactus-theme/。
所以整体流程就是:
1 | package.json 提供命令入口 |
如果只有 package.json,就只能写很长的一行命令,复制目录和安全清理都不好处理。
如果只有 PowerShell 脚本,每次都要手动记路径和执行方式,不方便发布。
因此最终固定为:
1 | npm run build:dual |
和:
1 | npm run deploy:dual |
踩坑记录
1. PowerShell 里 –config 要加引号
错误写法:
1 | hexo generate --config _config.yml,_config.cactus.yml |
在 PowerShell 里,这个参数可能会被拆坏,导致 Hexo 报:
1 | WARN Config file _config.yml _config.cactus.yml not found, using default. |
正确写法:
1 | hexo --config "_config.yml,_config.cactus.yml" generate |
2. 不要把 _config.butterfly.yml 当站点配置合并
错误写法:
1 | hexo --config "_config.yml,_config.butterfly.yml" generate |
这个写法看起来很合理,但会带来一个隐藏问题:_config.yml 和 _config.butterfly.yml 里都有 subtitle。
根目录 _config.yml 里的 subtitle 是站点副标题:
1 | subtitle: '异境入侵者' |
而 _config.butterfly.yml 里的 subtitle 是 Butterfly 首页打字机配置:
1 | subtitle: |
当使用 --config "_config.yml,_config.butterfly.yml" 时,后面的 Butterfly 配置会覆盖前面的站点配置。
于是 config.subtitle 从字符串变成了对象,网页标题就会变成:
1 | <title>YJRQZ777 - [object Object]</title> |
所以 Butterfly 不需要这样合并配置。
正确写法:
1 | hexo generate |
因为 Hexo 会根据根目录 _config.yml 里的:
1 | theme: butterfly |
自动加载:
1 | _config.butterfly.yml |
作为 Butterfly 的主题配置。
另外,还有一种错误写法:
1 | hexo --config "_config.butterfly.yml" generate |
这会只读取 Butterfly 配置,而不会读取根目录 _config.yml。
结果就是站点标题、作者、部署配置等主配置都丢了,标题可能会变成默认的 Hexo。
3. cactus 需要显式合并配置
cactus 是额外生成的子站,所以它需要指定自己的配置:
1 | hexo --config "_config.yml,_config.cactus.yml" generate |
这里 _config.cactus.yml 会覆盖根配置里的主题、输出目录和根路径:
1 | theme: cactus |
这个场景和 Butterfly 不同。Butterfly 是主站默认主题,Hexo 会自动加载它的主题配置;cactus 是额外构建目标,所以要手动合并配置。
4. 两个主题不能随便共用 public
一开始尝试让两个主题都输出到 public/,后来发现容易出问题。
例如 Butterfly 页面需要自己的:
1 | /js/main.js |
cactus 也有自己的:
1 | /js/main.js |
如果构建顺序或 public_dir 出错,可能出现 Butterfly 页面加载到 cactus 的 JS,导致顶部栏存在但透明不可见。
Butterfly 的导航默认是:
1 | #nav { |
需要 Butterfly 的 main.js 正常执行后加上 show 类:
1 | <nav id="nav" class="show"> |
如果加载错 JS,导航链接还在,但看起来像消失了一样。
5. cactus 的 root 必须和复制目录一致
现在 cactus 的配置是:
1 | root: /cactus-theme/ |
所以最终必须复制到:
1 | public/cactus-theme/ |
如果复制到:
1 | public/cactus/ |
那就要把 cactus 配置改成:
1 | root: /cactus/ |
这两个名字必须一致。
最终目录结构
构建完成后应该是:
1 | public/ |
发布时仍然只需要发布 public/。
GitHub Pages 看到的是一个普通静态站,但里面同时包含两个主题。
总结
Hexo 双主题的关键不是让一个主题“切换成另一个主题”,而是把它们当成两个静态站点构建。
Butterfly 负责主站:
1 | / |
cactus 负责子站:
1 | /cactus-theme/ |
两个主题独立生成,最后再组装到同一个 public/ 目录里发布。这样结构清楚,也能避免静态资源互相覆盖。