前言

Hugo 是纯静态博客,本身不具备评论功能,需要借助第三方评论系统。

最早我尝试自建 Artalk,在本地跑起来后,发现 GitHub Pages 强制走 HTTPS,而自建后端没有 SSL 证书,Mixed Content 导致评论直接加载失败。随后折腾了域名和 Let’s Encrypt 证书,结果京东云服务器没有备案,HTTPS 请求全部被拦截,彻底宣告失败。

兜兜转转,最终选择了 Giscus——基于 GitHub Discussions 的评论系统,零运维、完全免费,与 GitHub Pages 天然契合,非常适合技术博客使用。

评论系统选型

方案是否需要服务器是否需要 GitHub 账号数据存储
Artalk✅ 需要自建数据库
UtterancesGitHub Issues
GiscusGitHub Discussions
Twikoo可选(Vercel)MongoDB / LeanCloud
Waline可选(Vercel)LeanCloud 等

我的博客受众主要是开发者,使用 GitHub 账号登录评论完全合理,因此 Giscus 是最合适的选择。

Giscus 工作原理

Giscus 利用 GitHub Discussions API 将评论存储在仓库的 Discussions 中。每次加载文章页面时,前端脚本会通过 pathname 映射到对应的 Discussion 条目并渲染评论,用户使用 GitHub 账号授权后即可留言。

配置 Giscus

第一步:开启 GitHub Discussions

进入博客仓库 → SettingsFeatures,勾选 Discussions

第二步:获取仓库 ID 和分类 ID

访问 giscus.app,填入仓库名(如 username/username.github.io),Discussion 分类建议选 Announcements(仅维护者可新建,防止滥用)。配置完成后,页面底部会生成如下脚本,其中 data-repo-iddata-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

参考资料