前言
Hugo 是纯静态博客,本身不具备评论功能,需要借助第三方评论系统。
最早我尝试自建 Artalk,在本地跑起来后,发现 GitHub Pages 强制走 HTTPS,而自建后端没有 SSL 证书,Mixed Content 导致评论直接加载失败。随后折腾了域名和 Let’s Encrypt 证书,结果京东云服务器没有备案,HTTPS 请求全部被拦截,彻底宣告失败。
兜兜转转,最终选择了 Giscus——基于 GitHub Discussions 的评论系统,零运维、完全免费,与 GitHub Pages 天然契合,非常适合技术博客使用。
评论系统选型
| 方案 | 是否需要服务器 | 是否需要 GitHub 账号 | 数据存储 |
|---|---|---|---|
| Artalk | ✅ 需要 | ❌ | 自建数据库 |
| Utterances | ❌ | ✅ | GitHub Issues |
| Giscus | ❌ | ✅ | GitHub Discussions |
| Twikoo | 可选(Vercel) | ❌ | MongoDB / LeanCloud |
| Waline | 可选(Vercel) | ❌ | LeanCloud 等 |
我的博客受众主要是开发者,使用 GitHub 账号登录评论完全合理,因此 Giscus 是最合适的选择。
Giscus 工作原理
Giscus 利用 GitHub Discussions API 将评论存储在仓库的 Discussions 中。每次加载文章页面时,前端脚本会通过 pathname 映射到对应的 Discussion 条目并渲染评论,用户使用 GitHub 账号授权后即可留言。
配置 Giscus
第一步:开启 GitHub Discussions
进入博客仓库 → Settings → Features,勾选 Discussions。
第二步:获取仓库 ID 和分类 ID
访问 giscus.app,填入仓库名(如 username/username.github.io),Discussion 分类建议选 Announcements(仅维护者可新建,防止滥用)。配置完成后,页面底部会生成如下脚本,其中 data-repo-id 和 data-category-id 是后续需要用到的关键值:
<script src="https://giscus.app/client.js"
data-repo="username/username.github.io"
data-repo-id="R_kgD..."
data-category="Announcements"
data-category-id="DIC_kwD..."
...>
</script>
第三步:在 Hugo 中集成
新建 layouts/partials/giscus.html,将 Giscus 脚本封装为 Hugo partial,并通过 config.yaml 中的参数注入 ID,便于后续维护:
<script src="https://giscus.app/client.js"
data-repo="{{ site.Params.giscus.repo }}"
data-repo-id="{{ site.Params.giscus.repoID }}"
data-category="{{ site.Params.giscus.category }}"
data-category-id="{{ site.Params.giscus.categoryID }}"
data-mapping="pathname"
data-strict="0"
data-reactions-enabled="1"
data-emit-metadata="0"
data-input-position="top"
data-theme="preferred_color_scheme"
data-lang="zh-CN"
data-loading="lazy"
crossorigin="anonymous"
async>
</script>
其中两个关键属性说明:
data-input-position="top":评论输入框显示在评论列表上方,体验更顺手data-loading="lazy":懒加载,仅当用户滚动到评论区附近时才加载 iframe,减少页面初始开销
第四步:同步亮/暗主题
PaperMod 支持亮暗模式切换,需要监听主题切换事件并通知 Giscus iframe 同步。由于开启了懒加载,iframe 插入 DOM 的时机不确定,因此使用 MutationObserver 等待 iframe 出现后再绑定事件:
function setGiscusTheme(theme) {
const iframe = document.querySelector('iframe.giscus-frame');
if (!iframe) return;
iframe.contentWindow.postMessage(
{ giscus: { setConfig: { theme } } },
'https://giscus.app'
);
}
function watchThemeToggle() {
const themeToggle = document.getElementById('theme-toggle');
if (!themeToggle) return;
themeToggle.addEventListener('click', () => {
setTimeout(() => {
const isDark = document.body.classList.contains('dark');
setGiscusTheme(isDark ? 'dark' : 'light');
}, 300);
});
}
const observer = new MutationObserver(() => {
if (document.querySelector('iframe.giscus-frame')) {
observer.disconnect();
watchThemeToggle();
}
});
observer.observe(document.body, { childList: true, subtree: true });
第五步:更新 comments.html
修改 layouts/partials/comments.html,将原来调用的 artalk.html 改为 giscus.html:
{{ if or .Params.comments .Site.Params.comments }}
<div class="article-comments">{{- partial "giscus.html" .}}</div>
{{ end }}
第六步:填写配置并开启评论
在 config.yaml 中填入对应参数:
params:
comments: true
giscus:
repo: "username/username.github.io"
repoID: "R_kgD..." # 从 giscus.app 获取
category: "Announcements"
categoryID: "DIC_kwD..." # 从 giscus.app 获取
注意每篇文章的 front matter 中如果有 comments: false,会覆盖全局配置,需要一并改为 comments: true。