Skip to content

添加评论系统

为你的文档站点添加评论功能,增强用户互动。

相关文档

本文为「代码配方」,提供快速集成步骤。如需深入了解各方案细节,请参考:

评论系统对比

系统特点适用场景
Giscus基于 GitHub Discussions,免费技术博客、开源项目
Waline自托管,功能丰富个人博客、社区
Twikoo腾讯云开发,简单易用国内用户为主
Utterances基于 GitHub Issues技术博客

Giscus 集成

准备工作

  1. 确保仓库是公开的
  2. 安装 Giscus GitHub App
  3. 在仓库设置中启用 Discussions

获取配置

访问 giscus.app 获取配置:

  • data-repo: 仓库地址
  • data-repo-id: 仓库 ID
  • data-category: 讨论分类
  • data-category-id: 分类 ID

创建组件

vue
<!-- .vitepress/theme/components/GiscusComments.vue -->
<script setup lang="ts">
import { useData } from 'vitepress'
import { watch, onMounted, ref } from 'vue'

const { isDark } = useData()
const container = ref<HTMLElement>()

const giscusConfig = {
  repo: 'your-username/your-repo',
  repoId: 'your-repo-id',
  category: 'Announcements',
  categoryId: 'your-category-id',
  mapping: 'pathname',
  reactionsEnabled: '1',
  emitMetadata: '0',
  inputPosition: 'top',
  lang: 'zh-CN'
}

const loadGiscus = (theme: string) => {
  if (!container.value) return
  
  container.value.innerHTML = ''
  
  const script = document.createElement('script')
  script.src = 'https://giscus.app/client.js'
  script.setAttribute('data-repo', giscusConfig.repo)
  script.setAttribute('data-repo-id', giscusConfig.repoId)
  script.setAttribute('data-category', giscusConfig.category)
  script.setAttribute('data-category-id', giscusConfig.categoryId)
  script.setAttribute('data-mapping', giscusConfig.mapping)
  script.setAttribute('data-reactions-enabled', giscusConfig.reactionsEnabled)
  script.setAttribute('data-emit-metadata', giscusConfig.emitMetadata)
  script.setAttribute('data-input-position', giscusConfig.inputPosition)
  script.setAttribute('data-theme', theme)
  script.setAttribute('data-lang', giscusConfig.lang)
  script.setAttribute('crossorigin', 'anonymous')
  script.async = true
  
  container.value.appendChild(script)
}

onMounted(() => {
  loadGiscus(isDark.value ? 'dark' : 'light')
})

watch(isDark, (dark) => {
  loadGiscus(dark ? 'dark' : 'light')
})
</script>

<template>
  <div class="giscus-container">
    <div ref="container" class="giscus" />
  </div>
</template>

<style scoped>
.giscus-container {
  margin-top: 3rem;
  padding-top: 2rem;
  border-top: 1px solid var(--vp-c-divider);
}

.giscus {
  min-height: 200px;
}
</style>

注册到布局

ts
// .vitepress/theme/index.ts
import { h } from 'vue'
import DefaultTheme from 'vitepress/theme'
import GiscusComments from './components/GiscusComments.vue'

export default {
  extends: DefaultTheme,
  Layout: () => {
    return h(DefaultTheme.Layout, null, {
      'doc-after': () => h(GiscusComments)
    })
  }
}

Waline 集成

安装依赖

bash
npm install @waline/client

创建组件

vue
<!-- .vitepress/theme/components/WalineComments.vue -->
<script setup lang="ts">
import { useData } from 'vitepress'
import { init, type WalineInstance } from '@waline/client'
import { onMounted, onUnmounted, watch, ref } from 'vue'

import '@waline/client/style'

const { page, isDark } = useData()
const container = ref<HTMLElement>()
let waline: WalineInstance | null = null

const initWaline = () => {
  if (!container.value) return
  
  waline?.destroy()
  
  waline = init({
    el: container.value,
    serverURL: 'https://your-waline-server.vercel.app',
    path: page.value.path,
    lang: 'zh-CN',
    dark: isDark.value ? 'dark' : 'light',
    login: 'enable',
    wordLimit: [0, 500],
    pageSize: 10,
    requiredMeta: ['nick', 'mail'],
    meta: ['nick', 'mail', 'link'],
    highlighter: true,
    emoji: [
      'https://unpkg.com/@waline/emojis@1.1.0/weibo',
      'https://unpkg.com/@waline/emojis@1.1.0/bmoji'
    ]
  })
}

onMounted(() => {
  initWaline()
})

watch(isDark, () => {
  initWaline()
})

onUnmounted(() => {
  waline?.destroy()
})
</script>

<template>
  <div class="waline-container">
    <div ref="container" />
  </div>
</template>

<style scoped>
.waline-container {
  margin-top: 3rem;
  padding-top: 2rem;
  border-top: 1px solid var(--vp-c-divider);
}
</style>

部署 Waline 服务端

推荐使用 Vercel 部署:

  1. 点击 一键部署
  2. 配置环境变量(数据库连接等)
  3. 获取服务端地址

控制显示页面

只在特定页面显示评论:

vue
<script setup lang="ts">
import { useData } from 'vitepress'
import GiscusComments from './GiscusComments.vue'

const { frontmatter } = useData()
</script>

<template>
  <GiscusComments v-if="frontmatter.comments !== false" />
</template>

在 frontmatter 中禁用:

yaml
---
title: 某个页面
comments: false
---

评论统计

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

const { page } = useData()
const commentCount = ref(0)

onMounted(async () => {
  // Giscus API
  const res = await fetch(
    `https://giscus.app/api/discussions?repo=your-repo&term=${page.value.path}`
  )
  const data = await res.json()
  commentCount.value = data.total_count
})
</script>

<template>
  <span class="comment-count">
    {{ commentCount }} 条评论
  </span>
</template>

参考链接

贡献者

加载中...

想要成为贡献者?

在 CNB 上参与贡献