前言
这篇是博客的第一篇正式文章,顺便把“从 0 到 1 把个人博客部署到 GitHub Pages”的完整过程记下来——包括环境、配置、CI 工作流,以及几个真的会卡住新手的坑和对应解法。后面自己或别人再部署同类静态站,照着抄就能少走弯路。
技术栈:Hexo 7.3 + hexo-theme-butterfly 5.6 + GitHub Pages(用户级)+ GitHub Actions。
一、技术选型:为什么是 Hexo + Butterfly
静态博客方案一大把(Hugo / Jekyll / Astro / VuePress…),选 Hexo + Butterfly 是因为它一次性满足了我的 5 条硬需求:
| 需求 |
Butterfly 表现 |
| 1. Markdown + LaTeX/KaTeX 公式 |
内置 mathjax 开关,装 hexo-filter-mathjax 即生效 |
| 2. 多媒体(图片 + 视频链接嵌入) |
支持标准 Markdown 图片;视频用 ` 包裹 iframe 即可 |
| 3. 相册 / 图集页 | 原生 `type: gallery` 页面,无需额外插件 |
| 4. 时间线 + 分类 / 标签 | 归档、分类、标签页开箱即用 |
| 5. 高可扩展性 + 插件丰富 | Hexo 插件生态成熟,主题配置项极细 |
结论:**想要“开箱即用 + 高度可定制”的中文博客,Butterfly 是省心首选**。
---
## 二、环境准备
1 2 3 4
| node -v npm -v git --version
|
> 小提示:如果本机有多个 Node 版本,优先用受管(managed)版本,避免全局污染。
---
## 三、初始化 Hexo 项目
1 2 3 4
| npm install -g hexo-cli hexo init blog cd blog npm install
|
目录结构(关键部分):
1 2 3 4 5 6 7 8 9 10 11
| blog/ ├── _config.yml # 站点主配置 ├── _config.butterfly.yml # 主题配置(主题单独文件) ├── source/ │ ├── _posts/ # 文章放这里 │ ├── gallery/ # 相册页 │ ├── about/ # 关于页 │ ├── categories/ # 分类页 │ └── tags/ # 标签页 ├── themes/butterfly/ # 主题源码(一般用 npm 包) └── package.json
|
安装 Butterfly 主题(推荐用 npm 包方式,便于版本管理):
1
| npm install hexo-theme-butterfly
|
然后在 `_config.yml` 里把主题指过去:
---
## 四、Butterfly 主题配置要点
主题配置写在 `_config.butterfly.yml`(**不是** `_config.yml`),几个最常用的开关:
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
| menu: - name: 首页 icon: fa-solid fa-house path: / - name: 归档 icon: fa-solid fa-archive path: /archives/ - name: 分类 icon: fa-solid fa-folder-open path: /categories/ - name: 标签 icon: fa-solid fa-tags path: /tags/ - name: 相册 icon: fa-solid fa-image path: /gallery/ - name: 关于 icon: fa-solid fa-address-card path: /about/
mathjax: enable: true
search: use: local
comments: use: null
|
相册页 `source/gallery/index.md`:
1 2 3 4
| --- title: 相册 type: gallery ---
|
---
## 五、GitHub Pages 仓库设置
1. 新建仓库,命名为 `<用户名>.github.io`(用户级 Pages 必须用这个命名,否则要走自定义域名)。
2. 本地 `_config.yml` 设好:
1 2 3
| url: https://aidon-chen.github.io root: / language: zh-CN
|
3. 推送后,到仓库 **Settings → Pages**,Source 选 `gh-pages` 分支(这个分支由 CI 自动生成并推送,见下一节)。
---
## 六、GitHub Actions 自动部署
核心思路:往 `main` 推代码 → GitHub 在云端 `npm install` → `hexo generate` → 推到 `gh-pages` → Pages 自动发布。**你本地连 Node 都不用装**。
文件:`.github/workflows/deploy.yml`(注意开头的 `.` 和全小写路径):
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 36 37 38
| name: Deploy Blog to GitHub Pages
on: push: branches: - main workflow_dispatch:
permissions: contents: write pages: write id-token: write
jobs: build-and-deploy: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4
- name: Setup Node.js uses: actions/setup-node@v4 with: node-version: "22" cache: npm
- name: Install dependencies run: npm install
- name: Build site run: npx hexo clean && npx hexo generate
- name: Deploy to GitHub Pages uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./public publish_branch: gh-pages
|
> ⚠️ **不要写 `npm ci`**,除非你确定 `package-lock.json` 和 `package.json` 严格同步(见坑 7.3)。新手用 `npm install` 更稳。
**创建方式**:在 GitHub 网页上 `Add file → Create new file`,路径填 `.github/workflows/deploy.yml`,粘贴上面内容,直接 commit 到 `main` 即可——**完全不需要本地命令行**。
---
## 七、踩坑实录(重点)
下面这几个坑都是实打实踩过的,按顺序排好。
### 7.1 KaTeX 公式不渲染
- **现象**:文章里写 `$$E=mc^2$$` 或行内 `$a^2+b^2=c^2$`,页面出来是纯文本,公式没渲染。
- **根因**:只开了 `_config.butterfly.yml` 里的 `mathjax.enable: true`,但**没装渲染插件** `hexo-filter-mathjax`,MathJax 的 CDN 根本没注入页面。
- **修复**:
1
| npm install hexo-filter-mathjax
|
并在**每篇需要公式的文章** front-matter 加 `mathjax: true`(全局开启有时不够,逐篇声明最稳)。本文开头就带了这个开关,下面验证一下行内 $a^2+b^2=c^2$ 和块级:
$$
\int_{-\infty}^{\infty} e^{-x^2}\,dx = \sqrt{\pi}
$$
能正常显示就说明通了。
### 7.2 视频 iframe 被剥离
- **现象**:在文章里贴 B 站 / YouTube 的 ``,发布后页面**只剩空白**,源码里 iframe 不见了。
- **根因**:Hexo 默认的 `hexo-renderer-marked` 用了 DOMPurify 做 XSS 清洗,**会把 iframe 当成危险标签剥掉**。
- **修复**:用 Hexo 的 `{% raw %}` 标签把 iframe 包起来,告诉渲染器“这块原样输出、别清洗”:
1 2 3
| {% raw %} <iframe src="//player.bilibili.com/player.html?bvid=BVxxxx" ...></iframe> {% endraw %}
|
注意:上面示例放在**代码块**里所以能原样显示;你实际写文章时要去掉代码块包裹,直接用 `{% raw %}...{% endraw %} 包住 iframe 才行。 |
7.3 npm ci 锁文件不同步
- 现象:CI 红叉,日志一堆
npm error Missing: xxx@x.x.x from lock file,退出码 1。
- 根因:
npm ci 是“严格安装”,要求 package-lock.json 和 package.json 逐字节一致。中途手动往 package.json 加依赖(比如 7.1 的 hexo-filter-mathjax)却没重跑 npm install 更新锁文件,CI 上就炸。
- 修复(二选一):
- 懒人法:把工作流里的
npm ci 改成 npm install(容错,自动补齐缺失包)。本文 6 节用的就是这招。
- 正规法:本地
npm install 重新生成锁文件 → 推上去 → 改回 npm ci,保证可复现。
7.4 本地终端连不上 GitHub
- 现象:
gh auth login / git push 直接报 dial tcp ...:443: connectex: ... failed to respond,但浏览器能正常上 GitHub。
- 根因:本机命令行网络出口到
github.com:443 超时(代理 / 防火墙限制),而浏览器走的是另一条通路。
- Workaround(无需修网络):
- 所有“建文件 / 改文件”操作走 GitHub 网页(仓库主人账号天然有
workflow 等权限,不受 CLI token scope 限制);
- 本文的
deploy.yml 和这篇文章本身,都能直接在网页 Add file 建好,commit 到 main 即触发部署。
- 等本地终端网络通了,再回归
git push 工作流。
八、总结
到这里,一个支持公式 / 多媒体 / 相册 / 分类标签、且推送即自动部署的个人博客就完整跑通了。当前状态: