Skip to content

07 · 博客增强(可选)

核心 6 步走完后博客已经可用。本篇是可选增强——评论系统、访问统计、自定义域名等。一旦博客有了"互动属性",会从"单方输出"变成"双向交流"。


一、评论系统(Giscus,强烈推荐)⭐

为什么选 Giscus

方案后端免费中文注释数据归属
GiscusGitHub Discussions✅ 完全免费你自己的 GitHub 仓库
UtterancesGitHub Issues✅ 免费你的 GitHub Issues
Disqus自家服务免费但有广告在 Disqus 那边
Waline自建 + LeanCloud免费LeanCloud(要备案,有限制)
自建(Comment, Cusdis 等)自己搭后端要服务器自己

Giscus 是博客评论的标准答案:不要数据库、不要服务器、读写直接走 GitHub 账号登录、所有评论数据托管在你自己的 Discussions 里——意味着永远不会被"评论平台关闭"绑架

Giscus 的工作流程

访问者打开博客文章

页面加载 giscus.app/client.js

读取你配置的 GitHub 仓库的 Discussions

按 mapping 规则(pathname/title 等)找到对应讨论串

显示评论 + 留言框

访问者点击 "Sign in with GitHub" → 留言

评论作为 Discussion comment 发到 GitHub

前置条件

1. GitHub 仓库必须公开

Discussions 功能依赖仓库公开。你的源仓库(如 mmqqdd/botc-learning-notes)已经是公开的就 OK。

2. 启用 Discussions

GitHub 仓库页 → SettingsGeneral → 滚动到 "Features" → 勾选 Discussions

3. 安装 giscus app

https://github.com/apps/giscusInstall → 选择仓库授权

自助生成配置

https://giscus.app/zh-CN

按页面引导填写:

字段选什么
仓库your-username/your-repo(必须已启用 Discussions + 装了 giscus app)
页面 ↔️ discussion 映射关系推荐 pathname(每个 URL 一个讨论串)
Discussion 分类推荐 Announcements(受限分类,避免 spam)
特性推荐勾选"启用反应",其他默认
主题首选颜色方案(让 Giscus 跟随系统暗色模式)

页面会自动生成 <script> 配置代码,里面有 data-repo-iddata-category-id 等,复制下来备用

VitePress 集成(核心)

VitePress 默认主题里没有评论系统,要通过自定义主题 + 槽位(slot)注入。

Step 1:创建组件文件

.vitepress/theme/Giscus.vue

vue
<script setup lang="ts">
import { ref, watch, onMounted, nextTick } from 'vue'
import { useData, useRoute } from 'vitepress'

const { isDark } = useData()
const route = useRoute()
const container = ref<HTMLDivElement | null>(null)

// [REPLACE] 把下面这些值改成 giscus.app 给你生成的
const GISCUS_CONFIG = {
  repo: 'your-username/your-repo',
  repoId: 'R_kgDOxxxxx',
  category: 'Announcements',
  categoryId: 'DIC_kwDOxxxxx',
  mapping: 'pathname',
  strict: '0',
  reactionsEnabled: '1',
  emitMetadata: '0',
  inputPosition: 'top',
  lang: 'zh-CN',
  loading: 'lazy',
} as const

function renderGiscus() {
  if (!container.value) return
  container.value.innerHTML = ''

  const script = document.createElement('script')
  script.src = 'https://giscus.app/client.js'
  script.crossOrigin = 'anonymous'
  script.async = true

  const attrs: Record<string, string> = {
    'data-repo': GISCUS_CONFIG.repo,
    'data-repo-id': GISCUS_CONFIG.repoId,
    'data-category': GISCUS_CONFIG.category,
    'data-category-id': GISCUS_CONFIG.categoryId,
    'data-mapping': GISCUS_CONFIG.mapping,
    'data-strict': GISCUS_CONFIG.strict,
    'data-reactions-enabled': GISCUS_CONFIG.reactionsEnabled,
    'data-emit-metadata': GISCUS_CONFIG.emitMetadata,
    'data-input-position': GISCUS_CONFIG.inputPosition,
    'data-theme': isDark.value ? 'dark' : 'light',
    'data-lang': GISCUS_CONFIG.lang,
    'data-loading': GISCUS_CONFIG.loading,
  }

  for (const [key, value] of Object.entries(attrs)) {
    script.setAttribute(key, value)
  }

  container.value.appendChild(script)
}

// 跟随 VitePress 的明暗主题切换
watch(isDark, (dark) => {
  const iframe = document.querySelector<HTMLIFrameElement>('iframe.giscus-frame')
  iframe?.contentWindow?.postMessage(
    { giscus: { setConfig: { theme: dark ? 'dark' : 'light' } } },
    'https://giscus.app',
  )
})

// SPA 路由切换时重新渲染(每篇文章独立讨论串)
watch(
  () => route.path,
  () => {
    nextTick(() => renderGiscus())
  },
)

onMounted(() => {
  renderGiscus()
})
</script>

<template>
  <div class="giscus-wrapper">
    <h3 class="giscus-title">💬 评论与建议</h3>
    <p class="giscus-hint">
      读完有想法?欢迎在下面留言。评论使用 GitHub 账号登录,会同步到本仓库的 Discussions。
    </p>
    <div ref="container" class="giscus" />
  </div>
</template>

<style scoped>
.giscus-wrapper {
  margin-top: 48px;
  padding-top: 24px;
  border-top: 1px solid var(--vp-c-divider);
}

.giscus-title {
  font-size: 18px;
  font-weight: 600;
  margin: 0 0 8px;
}

.giscus-hint {
  font-size: 14px;
  color: var(--vp-c-text-2);
  margin: 0 0 20px;
}
</style>

Step 2:扩展默认主题

.vitepress/theme/index.ts

typescript
import DefaultTheme from 'vitepress/theme'
import { h } from 'vue'
import type { Theme } from 'vitepress'
import Giscus from './Giscus.vue'

const theme: Theme = {
  extends: DefaultTheme,
  Layout() {
    return h(DefaultTheme.Layout, null, {
      // 评论组件插入到每篇文档末尾
      'doc-after': () => h(Giscus),
    })
  },
}

export default theme

Step 3:跑起来验证

bash
npm run dev

打开任意一篇文章,滚动到底部应该能看到评论框。


关键代码解读

为什么用 doc-after 槽位

VitePress 默认主题暴露了多个槽位(slot),文档页可用的有:

Slot位置
doc-top文档正文上方
doc-before正文之前
doc-after正文之后(评论的最佳位置)
doc-footer-before文档 footer 之前

doc-after 是评论组件的最佳位置——读者看完文章自然就到这里。

为什么要 watch(route.path) 重新渲染

VitePress 是 SPA。当用户从文章 A 切到文章 B:

  • URL 变了,但 Giscus 的 <iframe> 还是老的(保持 A 的评论)
  • 必须监听路由变化、重新执行 renderGiscus() 才能切换到 B 的评论

不加这一段会导致"换文章评论不变"的诡异行为。

为什么要 watch(isDark) 发 postMessage

Giscus 是嵌入的 iframe,不会自动跟随主页的暗色模式

通过 postMessage 给 iframe 发 setConfig 命令,可以动态切换 Giscus 主题——让评论框和博客本身的暗/亮模式保持一致。

不加这一段,用户切到暗色模式时,评论框还是白底,体验割裂。


常见坑

坑 1:评论框不显示

  • 检查仓库是公开的启用了 Discussions
  • 检查 giscus app 已安装到该仓库
  • 检查浏览器控制台有没有 4xx/5xx 错误(可能是 repoId 或 categoryId 写错了)

坑 2:换文章评论不变

→ 没监听 route.path 变化(参考上面的代码)

坑 3:暗色模式切换评论框还是亮色

→ 没监听 isDark + 发 postMessage(参考上面的代码)

坑 4:本地 dev 看到评论但部署后看不到

→ 通常是 Cloudflare Pages / Vercel 的 build 没拷贝 .vitepress/theme/ 目录。确认 .gitignore 没把 theme 目录排除

坑 5:Discussion 分类错误

giscus.app 自助生成时如果选了一个不限制写入的分类(如 General),任何 GitHub 用户都能直接 New Discussion 制造垃圾内容。推荐用 Announcements 这种受限分类——只允许评论,不允许新建话题。


收益与代价

收益

  • ✅ 文章下面立刻有了"反馈渠道"
  • ✅ 反应(emoji)功能让读者无压力表达喜好
  • ✅ 所有评论数据沉淀在你自己的 GitHub Discussions
  • ✅ 评论历史和代码版本一起备份

代价

  • ⚠️ 评论需要 GitHub 账号登录(部分非技术读者会被劝退)
  • ⚠️ 国内访问 giscus.app 偶发抽风(但比 Vercel 强)

二、访问统计(推荐)

待补充。可选方案:Cloudflare Web Analytics(免费、隐私友好)、Plausible、Umami(自建)。


三、自定义域名

参见 05-deploy.md 第八章


四、自定义 CSS / 主题

VitePress 默认主题足够用。需要小改时可在 .vitepress/theme/index.ts 引入自定义 CSS:

typescript
import './custom.css'

待补充:常用自定义场景(字号、字体、颜色等)。


五、SEO 优化

待补充。VitePress 默认有 <title> / <meta description>,进阶可加 sitemap、Open Graph 等。


总结

"功能" 不是越多越好——
每加一个功能都要问:
是不是真的能让"读者 → 创作者"的反馈环短一点?

评论系统能短,所以加;
访问统计某种程度能短,可以加;
留言板、聊天室、订阅 push……大概率不能短,慎加。

回到 首页 看其他章节。

💬 评论与建议

读完有想法?欢迎在下面留言。评论使用 GitHub 账号登录,会同步到本仓库的 Discussions。

基于本 playbook 自身搭建 · 部署于 Cloudflare Pages