最近给博客添加了一个「微博文」功能,可以发布短内容、想法、观点,类似朋友圈或微博。这篇文章记录了设计思路、实现过程和遇到的问题。

需求背景

博客通常用来写长文章,但很多时候只想记录一些短想法、碎碎念,不值得写一篇完整的文章。微博文功能就是为了解决这个问题:

  • 发布短内容,支持文字和图片
  • 手机端也能方便发布
  • 在首页侧边栏显示预览
  • 有独立的聚合页面

技术方案选型

内容存储

考虑了几个方案:

  1. 自建后端服务 - 需要服务器、数据库,维护成本高
  2. 第三方评论系统 - 如 Valine、Waline,但数据不在自己手里
  3. GitHub Issues - 免费、稳定、支持 Markdown、手机可通过 GitHub App 操作

最终选择了 GitHub Issues,理由:

  • 零成本,不需要额外服务器
  • 数据完全可控,就在自己的仓库里
  • GitHub API 完善,方便读取
  • 手机可以通过 GitHub App 或网页发布

前端展示

使用 Hexo 的数据文件功能,在构建时生成静态页面:

  • source/_data/shuoshuo.yml 存储微博文数据
  • 自定义 Pug 模板渲染页面
  • JavaScript 实现首页侧边栏预览

实现细节

1. 创建独立的 GitHub Action

为了方便复用,我创建了一个独立的 GitHub Action 仓库:hexo-moments-action

核心功能

  • 调用 GitHub API 获取带指定标签的 Issues
  • 解析 Issue 内容,支持 Markdown 图片语法
  • 生成 Hexo 数据文件 shuoshuo.yml

Action 的核心逻辑

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
// 从 GitHub API 获取 Issues
async function fetchIssues() {
const response = await fetch(
`https://api.github.com/repos/${owner}/${repo}/issues?labels=${label}&state=open&sort=created&direction=desc`,
{ headers: { Authorization: `token ${token}` } }
);
return response.json();
}

// 解析内容,提取图片
function parseContent(body) {
const imageRegex = /!\[.*?\]\((.*?)\)/g;
const images = [...body.matchAll(imageRegex)].map(m => m[1]);
const content = body.replace(imageRegex, '').trim();
return { content, images };
}

// 生成 YAML 数据文件
function generateYaml(issues) {
return issues.map(issue => ({
date: issue.created_at,
content: parseContent(issue.body).content,
tags: issue.labels.map(l => l.name).filter(n => n !== label),
key: `moment-${issue.number}`
}));
}

Action 的优势

  • 可被任何 Hexo 博客复用
  • 支持自定义标签、输出路径等配置
  • 输出 has-changes 标识,方便后续流程判断

2. 在博客仓库中配置 Workflow

创建 .github/workflows/moments.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
39
name: Sync Moments

on:
issues:
types: [opened, edited, closed, reopened, labeled, unlabeled]
workflow_dispatch:

permissions:
contents: write
issues: read

jobs:
sync:
runs-on: ubuntu-latest
if: github.event_name == 'workflow_dispatch' || contains(github.event.issue.labels.*.name, 'moment')

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Sync moments
uses: panghutx/hexo-moments-action@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Commit source changes
if: steps.sync.outputs.has-changes == 'true'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add source/_data/shuoshuo.yml
git commit -m "chore: sync moments from issues [skip ci]"
git push

- name: Generate and Deploy
run: |
npx hexo clean
npx hexo generate
npx hexo deploy

3. 页面模板

在 Butterfly 主题中创建了两个 Pug 模板:

media.pug - 首页侧边栏预览组件:

1
2
3
4
5
6
7
8
#moments-widget
.moments-header
a(href='/moments/') 动态
.moments-content
each moment in site.data.shuoshuo.slice(0, 5)
.moment-item
.moment-date= date(moment.date, 'MM-DD')
.moment-text= moment.content

shuoshuo.pug - 独立页面完整列表:

1
2
3
4
5
6
7
8
each moment in site.data.shuoshuo
article.shuoshuo-item
.meta
time= date(moment.date, 'YYYY-MM-DD')
if moment.tags
each tag in moment.tags
span.tag= tag
.content!= moment.content

4. JavaScript 增强

首页侧边栏通过 JavaScript 动态加载最新内容:

1
2
3
4
5
6
async function loadMoments() {
const response = await fetch('/moments/');
// 解析页面,提取微博文数据
const moments = parseMomentsFromHtml(html);
renderMomentsWidget(moments);
}

遇到的问题

问题一:Git Submodule 导致主题文件丢失

现象:本地预览正常,但 GitHub Actions 部署后页面空白,控制台报 “No layout” 错误。

原因:Butterfly 主题是作为 Git Submodule 引入的,指向官方仓库。但官方仓库没有我自定义的 media.pugshuoshuo.pug 模板文件。GitHub Actions checkout 时只拉取了官方版本,丢失了本地修改。

解决方案:将主题从 Submodule 转换为普通目录:

1
2
3
4
5
6
7
8
9
# 删除 submodule 的 .git 目录
rm -rf themes/butterfly/.git

# 从 git 索引中移除 submodule 引用
git rm --cached themes/butterfly

# 将主题文件作为普通文件添加
git add themes/butterfly/
git commit -m "fix: convert butterfly theme from submodule to regular files"

问题二:SSH 部署失败

现象:GitHub Actions 部署到服务器时报 “Permission denied”。

原因:缺少 SSH 私钥配置。

解决方案

  1. 在 GitHub 仓库设置中添加 SSH_PRIVATE_KEY secret
  2. 在 workflow 中添加 SSH 配置步骤:
1
2
3
4
5
6
- name: Setup SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H your-server-ip >> ~/.ssh/known_hosts

问题三:Git 用户未配置

现象:部署时报错 “Please tell me who you are”。

解决方案:在 workflow 中配置 git 用户:

1
2
3
4
5
- name: Generate and Deploy
run: |
git config --global user.name "your-name"
git config --global user.email "your-email"
npx hexo deploy

问题四:Workflow 文件放在错误的位置

现象:在博客静态仓库(GitHub Pages 仓库)中添加了 .github/workflows/moments.yml,但 Issue 创建后没有任何反应。

原因:Hexo 部署时只会将 public/ 目录下的静态文件推送到 GitHub Pages 仓库,.github/workflows 目录根本不会被上传。GitHub Actions 只能在源码仓库中触发。

解决方案:创建独立的源码仓库 hexo-blog,将 workflow 文件放在源码仓库中。最终形成了两个仓库:

  • hexo-blog:源码仓库,包含 workflow、主题、配置等
  • blog:静态仓库,只包含生成的静态文件(GitHub Pages)

问题五:Workflow 只更新源码,不更新博客

现象:Workflow 运行成功,shuoshuo.yml 文件也更新了,但博客页面没有变化。

原因:Workflow 更新了源码仓库中的数据文件,但没有触发 Hexo 重新构建和部署。需要在同一个 workflow 中完成:同步数据 → 构建 → 部署。

解决方案:在 workflow 中添加构建和部署步骤:

1
2
3
4
5
6
7
8
9
10
11
steps:
- name: Sync moments
uses: panghutx/hexo-moments-action@v1

- name: Commit source changes
# 更新源码仓库
- name: Generate and Deploy
run: |
npx hexo clean
npx hexo generate
npx hexo deploy # 部署到静态仓库

这样整个流程就打通了:Issue 更新 → 同步数据 → 提交源码 → 构建静态文件 → 部署到 GitHub Pages。

最终效果

  • 发布方式:在 GitHub 仓库创建 Issue,添加 moment 标签
  • 首页预览:侧边栏显示最新的 5 条微博文
  • 独立页面/moments/ 显示完整列表,支持标签分类
  • 自动化:Issue 创建或更新后自动构建部署

总结

本文介绍了如何为 Hexo 博客添加微博文功能:

  1. 方案选择:使用 GitHub Issues 作为内容存储,零成本且数据可控
  2. 核心实现:创建独立的 GitHub Action 同步 Issues 数据,生成 Hexo 数据文件
  3. 前端展示:自定义 Pug 模板渲染页面,JavaScript 实现首页侧边栏预览
  4. 自动化流程:Issue 更新后自动同步、构建、部署

相关代码仓库

如果你也想在 Hexo 博客添加类似功能,可以直接使用 panghutx/hexo-moments-action,只需在源码仓库中添加 workflow 配置即可。