从语雀到GitHub Pages:自动同步部署博客全流程
项目背景
一直用语雀写笔记和技术文档,但语雀的公开分享体验一般,也没有独立域名。于是搭建了一套自动化流水线:语雀写作 → Elog 同步 → GitHub Actions → Hexo 构建 → GitHub Pages 发布,全程自动,零成本。
线上博客:blog.ahao430.site
语雀知识库:yuque.com/ahao430/zsnfd6
整体架构
[图:架构流程图 - 语雀 → Elog → GitHub Actions → Hexo → GitHub Pages]
1 | 语雀写作 ──(Elog 定时同步)──> GitHub 仓库 master 分支 |
两个 GitHub Actions 工作流各司其职:
| 工作流 | 触发方式 | 职责 |
|---|---|---|
| Sync Yuque | 定时(每 6 小时)/ 手动 | Elog 同步语雀 → docs/,刷新 README 目录 |
| Deploy Hexo | Sync 完成后自动 / git tag deploy-* / 手动 |
注入 frontmatter → Hexo 构建 → 推送 gh-pages |
第一步:Elog 同步语雀
为什么选 Elog
Elog 是一个开源的多平台写作同步工具,支持语雀、Notion、飞书等。选它的原因:
- 账号密码模式:不需要语雀 Token(Token 需要超级会员),直接用账号密码登录
- CLI 友好:
elog sync一条命令搞定 - 缓存机制:通过
elog.cache.json记录文档元数据(标题、更新时间、分类),后续构建 frontmatter 全靠它
配置
elog.config.json:
1 | { |
关键配置说明:
onlyPublished: true:只同步已发布的文档,草稿不同步outputDir: "docs":同步到仓库的docs/目录filename: "title":用文档标题作为文件名
GitHub Actions 定时同步
sync.yml 每 6 小时运行一次:
1 | on: |
分钟选 17 而不是 0,是 GitHub Actions 的一个小技巧:避开整点高峰,减少排队等待时间。
语雀的账号密码通过 GitHub Secrets 传入:
1 | - name: Sync from Yuque |
同步完成后,tools/build-readme.cjs 读取 elog.cache.json 中的元数据,自动生成包含 72 篇文章目录的 README.md。
参考:Elog 文档
第二步:Hexo 构建 + frontmatter 注入
为什么不在语雀文档里写 frontmatter
语雀的 Markdown 编辑器不支持 YAML frontmatter,而且手动维护 72 篇文章的 frontmatter 不现实。解决思路是:构建期自动注入。
prepare-posts 脚本
tools/prepare-posts.cjs 在 Hexo 构建前运行,核心逻辑:
- 读取
elog.cache.json,获取每篇文章的标题、日期、分类 - 从
docs/读取原始 Markdown - 自动拼接 frontmatter(title、date、updated、categories)
- 输出到
source/_posts/
1 | fm = [ |
Nunjucks 模板冲突问题
这是整个项目踩过最深的坑。
Hexo 使用 Nunjucks 作为模板引擎,它会处理 {{ }} 和 {% %} 语法。而我的语雀文档中有大量小程序模板代码,恰好使用了相同的语法:
1 | <!-- 小程序 WXML 中的变量绑定,与 Nunjucks 冲突 --> |
踩坑一:{% raw %} 导致 Markdown 完全不渲染
最初的做法是用 {% raw %}...{% endraw %} 包裹整个正文,防止 Nunjucks 误解析。结果发现 Hexo 8.x 中,{% raw %} 会阻止内置 Markdown 渲染器的执行,导致:
→ 输出原始语法,不渲染为<img>标签## 标题→ 输出原始语法,不渲染为<h2>标签- 所有 Markdown 格式全部失效
最终方案:占位符转义 + 后处理还原
在 prepare-posts.cjs 中用不会与 Markdown 冲突的 Unicode 字符做占位符:
1 | body = body |
Hexo 构建完成后,通过 after_render:html filter 还原:
1 | // scripts/escape-restore.js |
选择 ‹›(单引号角括号,U+2039/U+203A)的原因是它们不会被 Markdown 或 HTML 编码器转义,能原样保留到最终 HTML 中。
第三步:Butterfly 主题 + Utterances 评论
主题选择
最初用的是 Hexo 默认的 Landscape 主题,功能较基础。换到了 Butterfly,理由:
- 开箱即用的暗黑模式
- 丰富的侧边栏组件
- 内置多种评论系统支持
- 活跃的社区维护
配置了导航菜单连接 GitHub 和语雀:
1 | menu: |
Utterances 评论
Utterances 是一个基于 GitHub Issues 的轻量评论系统:
- 无需数据库、无需登录
- 评论数据存储在仓库的 Issues 中
- 支持暗黑模式切换
Butterfly 中只需一行配置:
1 | utterances: |
注意:需要在仓库 Settings → General → Features 中开启 Issues。
第四步:自定义域名
[图:GitHub Pages 自定义域名设置页面截图位置]
DNS 配置
域名托管在 Cloudflare,添加一条 CNAME 记录:
| 类型 | 名称 | 目标 |
|---|---|---|
| CNAME | blog | ahao430.github.io |
不要开启 Cloudflare 代理(橙色云朵),否则 GitHub 无法正确识别自定义域名,导致 HTTPS 证书签发失败。
域名验证
GitHub Pages 要求在账号级别验证域名所有权:
- GitHub → Settings → Pages → Add a domain → 输入
blog.ahao430.site - 按提示添加 TXT 记录:
_github-pages-challenge-ahao430.blog.ahao430.site - 等待 DNS 生效后完成验证
- 回到仓库 Settings → Pages → Custom domain 填入域名
验证通过后,GitHub 会自动签发 Let’s Encrypt 证书(约 5-10 分钟),然后勾选 Enforce HTTPS。
CNAME 文件
Hexo 的 source/CNAME 文件会在每次构建时复制到 public/CNAME,随部署推到 gh-pages 分支,确保自定义域名配置不丢失。
第五步:语雀图片防盗链处理
[图:语雀 CDN Referer 防盗链导致的 403 截图位置]
语雀的图片托管在 cdn.nlark.com,这个 CDN 做了 Referer 防盗链:
| 请求来源 | HTTP 状态 |
|---|---|
| 无 Referer | 200 OK |
Referer: www.yuque.com |
200 OK |
Referer: blog.ahao430.site |
403 Forbidden |
浏览器在加载图片时会自动带上当前页面的 Referer,导致从博客域名访问时图片全部 403。
解决方案:在 HTML <head> 中添加 <meta name="referrer" content="no-referrer">。
在 Butterfly 主题中通过 inject.head 实现:
1 | inject: |
这样浏览器在加载图片时不发送 Referer,语雀 CDN 就不会拦截。
第六步:主题覆盖导致 0 字节页面
另一个让人排查了好一阵的问题。
为注入 referrer meta 标签,最初尝试了在 themes/landscape/layout/_partial/head.ejs 中创建局部覆盖文件。结果 Hexo 检测到本地 themes/landscape/ 目录后,认为整个主题都在本地,不再去 node_modules/ 中加载,而本地只放了一个 head.ejs 文件,缺少 layout.ejs、index.ejs 等主要模板。
后果:所有页面生成时报 “No layout” 警告,index.html 为 0 字节,网站白屏。
教训:覆盖主题文件要复制完整主题,或者用 Hexo 的 filter/injector API 注入内容。
工作流触发优化
为了方便调试,在 deploy 工作流中增加了 git tag 触发:
1 | on: |
推送 deploy-v* 标签即可触发部署,不需要在 GitHub 网页上手动点按钮:
1 | git tag deploy-v4 && git push origin deploy-v4 |
注意:push tag 事件需要额外的 condition 判断,因为
workflow_run.conclusion只在workflow_run事件中存在。
项目依赖一览
| 工具 | 用途 | 链接 |
|---|---|---|
| Elog | 语雀同步 | github.com/Lete114/elog |
| Hexo 8.x | 静态站点生成 | hexo.io |
| hexo-theme-butterfly | 博客主题 | butterfly.js.org |
| hexo-renderer-marked | Markdown 渲染 | github.com/hexojs/hexo-renderer-marked |
| Utterances | 评论系统 | utteranc.es |
| GitHub Actions | CI/CD | docs.github.com/actions |
| GitHub Pages | 静态托管 | pages.github.com |
| Cloudflare | DNS 托管 | cloudflare.com |
总结
这套方案实现了「语雀写 → 自动同步 → 自动构建 → 自定义域名发布」的全自动化闭环,成本和维护负担都很低:
- 内容管理:继续用语雀熟悉的编辑器
- 同步:无需手动操作,每 6 小时自动同步
- 发布:同步完成后自动部署
- 成本:零(GitHub Pages 免费、Cloudflare DNS 免费、Elog 开源)
- 维护:新增文章无需任何额外操作
后续计划:接入 Algolia 搜索、优化移动端阅读体验。