一次 Hexo 布局崩溃的排查记录:同名样式文件覆盖了主题

把博客从 WordPress 换到 Hexo + icarus 主题之后,我开始给博客加自定义样式。结果改完一部署,页面布局整个崩了:导航栏样式没了、卡片排版乱掉,几乎是”裸奔”状态。这篇文章记录一下这次事故的排查过程,以及 icarus 主题下自定义 CSS 的正确姿势。

事故现象

我给博客加了一套自定义配色(比如把链接改成蓝色 #2563eb),按直觉放在了一个很”合理”的位置:

1
blog/source/css/default.css   ← 站点源码目录下新建的样式文件

然后 git push,等 CI 构建部署完,打开页面一看——布局全崩了。主题的卡片、导航栏、侧边栏样式全部消失,只剩下最基本的内容流。用浏览器检查一下,发现加载的 /css/default.css 只有区区几行(就是我自己写的那几句配色),整个主题的样式表不翼而飞。

排查过程

第一反应是 CI 构建缓存问题,hexo clean 重新构建,无效。于是直接抓取线上构建产物:

1
2
curl -s https://www.makiblog.cn/css/default.css | wc -c
# 输出:181 ← 只有我自己写的几行

正常情况这个文件应该有几百 KB(主题样式 + 自定义样式编译产物)。对比发现,线上加载的 default.css 就是我自己建的那个文件。

到这里基本定位了:我在站点 source/css/ 下新建的 default.css,和主题编译产物 重名 了。

根因

icarus 主题的样式源文件是:

1
themes/icarus/source/css/default.styl

hexo 生成时,会把主题的 .styl 编译成静态资源,输出路径恰好是:

1
public/css/default.css

而我手贱在站点源码目录也建了同名文件:

1
blog/source/css/default.css

hexo 对同名静态资源的处理规则是:站点 source/ 下的同名文件覆盖主题编译产物。于是发布后线上 /css/default.css 被我的几行配色顶掉,主题几百 KB 的样式全部丢失,布局自然崩了。

修复

正确的做法是:不要动主题的编译产物,而是去改主题的样式源文件。icarus 主题本身支持多个配色变体(default.css、cyberpunk.css 等),这些变体都在主题源码里,通过 _config.yml 的 style_variant 配置选择。

自定义样式有两种推荐姿势:

  1. 修改主题源码:在 themes/icarus/source/css/ 下新建 makiblog.styl,然后在 default.styl 末尾追加 @import 'makiblog'。这样编译时主题样式和自定义样式一起进同一个 default.css,互不覆盖。
  2. 只加不改:自定义内容很独立时,可以直接在主题配置里指向自己的变体文件,别去占用 default.css 这个名字。

我采用了第一种,最终结构是这样:

1
2
3
themes/icarus/source/css/
├── default.styl ← 主题样式源,末尾追加 @import 'makiblog'
└── makiblog.styl ← 我的自定义样式

修改后重新构建,/css/default.css 恢复为几百 KB(主题样式 + 自定义配色),布局恢复正常。

经验总结

  1. 不要和主题编译产物重名。hexo 站点 source/ 目录下的同名文件会覆盖主题资源,这是”按直觉做事”最容易踩的坑。
  2. 自定义样式优先改主题源码。对 icarus 来说,正确入口是 themes/icarus/source/css/ 下的 .styl 文件,改完 @import 进去即可,干净且不破坏主题升级。
  3. 排查时先看产物再怀疑缓存。curl 线上文件对比大小,比反复 hexo clean 高效得多。

这次的教训说白了就一句:在没搞清文件命名空间之前,别轻易在源码目录里建”看起来很合理”的同名文件。

从 WordPress 到 GitHub Pages:一次博客平台的"换轨"记录

从 WordPress 到 GitHub Pages:一次博客平台的”换轨”记录

折腾了两天,我的博客终于完成了从「云服务器 + WordPress」到「GitHub Pages + Hexo」的平台切换。这篇文章记录一下切换的过程、分支迁移的思路,以及一些踩坑经验,希望能帮到同样想”换轨”的朋友。

为什么要换

原来的方案是:腾讯云服务器 + 宝塔面板 + WordPress。这套方案本身很成熟,功能强大,但对我这种以技术笔记为主的轻量博客来说,有几个痛点:

  • 成本:云服务器每年要续费,域名解析、备案、安全组配置都挂在服务器上。
  • 维护负担:WordPress 生态需要关注 PHP 版本、插件安全更新、备份策略,对”只想安静写文章”的人来说是额外的心理负担。
  • 内容形态:我的博客内容几乎全是 Markdown,而 WordPress 的富文本编辑器反而不如本地编辑器顺手。

而 GitHub Pages + Hexo 的组合恰好对症:免费托管、原生 Markdown、Git 版本控制、push 即部署。缺点是需要接受”静态站点”的定位,以及托管在境外平台这一事实——关于备案,后面单独说。

新旧架构对比

维度 旧方案 新方案
托管 腾讯云服务器 GitHub Pages
建站 WordPress + 宝塔 Hexo + icarus 主题
写作 浏览器后台 本地 Markdown
部署 手动/脚本 push 即自动构建
费用 服务器年费 + 域名 域名 + 0 服务器费用
备案 ICP + 公安备案 域名保留,DNS 转发接入

域名 makiblog.cn 没有变,只是把 DNS 解析从”指向服务器 IP”改成了”转发到 gray-o-gray.github.io”。原先的 ICP 备案和公安备案依然保留——域名还在使用,备案信息就继续有效。

分支迁移:两个分支的合流

这次切换最核心的部分是 Git 分支的处理。旧的博客仓库一直保持”双分支”结构:

  • hexo 分支:Hexo 源码(文章、主题配置、构建脚本)
  • master 分支:构建产物(hexo generate 生成的静态文件)

旧流程是本地手动构建:hexo d -g,然后由 hexo-deployer-git 把产物推送到 master 分支,Pages 从 master 直接提供静态文件。写一篇文章要走”源码分支 → 本地构建 → 产物分支”三个环节,中间还得保证本地 Node 环境版本一致,一旦换电脑或者忘记 hexo clean,就容易出一些莫名其妙的构建差异。

既然决定引入 GitHub Actions 做自动部署,我索性把两个分支”合流”:源码直接放 master,push 后由 CI 自动构建,产物由 GitHub Pages 的 Actions 托管。

这样做的好处很直接:

  1. 心智负担降到最低:只有一个分支,写文章、提交、推送,三个动作全部发生在 master。
  2. 不再依赖本地环境:构建在 CI 的 Ubuntu 环境里完成,本地只需一个编辑器。
  3. 产物永不脏:master 上只有源码,每次部署的产物都是全新构建的。

迁移本身很朴素:在 master 上删掉旧的产物文件,把 hexo 分支的内容复制过来,生成一个新的迁移 commit(保留历史,不需要 force push)。

GitHub Actions 自动部署

部署 workflow 的核心逻辑是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
name: Deploy Hexo Site to GitHub Pages

on:
push:
branches: [ master ]

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20

- name: Install dependencies
run: |
cd blog
npm install

- name: Build site
run: |
cd blog
npx hexo generate

- name: Add CNAME
run: echo "www.makiblog.cn" > blog/public/CNAME

- name: Deploy to GitHub Pages
uses: actions/deploy-pages@v4

几个关键点:

  • **submodules: recursive**:我的 icarus 主题是通过 git submodule 引入的,构建时如果不拉取子模块,生成会直接失败。
  • CNAME:自定义域名需要 CNAME 文件出现在构建产物里,所以构建后单独写一次,比手动维护 source/CNAME 更不容易遗漏。
  • **actions/deploy-pages**:这是 GitHub 官方的 Pages 部署 action,配合 Pages 的 “Source: GitHub Actions” 设置,部署完全由 CI 掌控,不再依赖某个分支的静态文件。

踩坑记录

  1. 子模块主题拉取失败:第一次构建报错找不到主题,就是忘了在 checkout 时加 submodules: recursive。
  2. _config.yml 里的占位 url:Hexo 初始化时默认是 http://example.com,不改成真实域名会导致 sitemap、og 标签、canonical 链接全部指向错误地址。
  3. Pages Source 切换:从”部署分支”切到”GitHub Actions”后,自定义域名有可能需要重新在 Settings → Pages 里确认一次,并等 HTTPS 证书重新签发。

现在的发布流程

切换完成之后,发布一篇文章只需要三步:

1
2
3
写文章(blog/source/_posts/)
git add + git commit
git push

push 出去 1-2 分钟,站点自动更新。本地不用装 Node、不用跑构建,换任何一台电脑只要 git 和编辑器就能继续写。

结语

这次”换轨”最大的收获,是把”写博客”这件事从”运维一台服务器”里剥离了出来。GitHub Actions 自动部署让发布变成了纯粹的写作行为,而 Git 历史天然就是文章的版本管理。

平台的切换和 git 分支的合流其实是一回事:把复杂的东西收拢到一条主线里,让日常的操作路径短一点、确定一点。剩下的精力,就留给内容本身吧。

You need to set client_id and slot_id to show this AD unit. Please set it in _config.yml.