在 GitHub Pages 这样的平台上托管精简的静态博客有很多优点,但也牺牲了一些交互性。幸运的是,Giscus 的出现提供了一种在静态网站上嵌入用户评论的方法。
Giscus 如何工作
Giscus 使用 GitHub API 读取并存储 GitHub 用户在与仓库关联的 Discussions(讨论区)中发表的评论。
在你的网站上嵌入 Giscus 客户端脚本包,配置正确的仓库 URL,用户(登录 GitHub 后)即可查看和发表评论。
这种方法是无服务器的,因为评论存储在 GitHub 上并从客户端动态加载,因此非常适合像 AstroPaper 这样的静态博客。
设置 Giscus
Giscus 可以很容易地在 giscus.app 上设置,但我仍会简要概述该过程。
前提条件
让 Giscus 工作的先决条件是:
- 仓库是公开的
- 已安装 Giscus 应用
- 你的仓库已开启 Discussions 功能
如果由于某种原因无法满足这些条件中的任何一个,遗憾的是,将无法集成 Giscus。
配置 Giscus
接下来,需要配置 Giscus。在大多数情况下,预选的默认值是合适的,除非你有特定原因并且知道自己在做什么,否则只需修改它们即可。不必太担心做错选择;你以后随时可以调整配置。
但是你需要:
- 选择正确的 UI 语言
- 指定你要连接的 GitHub 仓库,通常是在 GitHub Pages 上托管你的 AstroPaper 博客的仓库
- 如果你想确保没有人可以直接在 GitHub 上创建随机评论,请在 GitHub 上创建并设置一个
Announcement(公告)类型的讨论 - 定义配色方案
配置好设置后,Giscus 会为你提供一个生成的 <script> 标签,你在接下来的步骤中会用到它。
简单的 script 标签
你现在应该有一个看起来像这样的 script 标签:
<script
src="https://giscus.app/client.js"
data-repo="[ENTER REPO HERE]"
data-repo-id="[ENTER REPO ID HERE]"
data-category="[ENTER CATEGORY NAME HERE]"
data-category-id="[ENTER CATEGORY ID HERE]"
data-mapping="pathname"
data-strict="0"
data-reactions-enabled="1"
data-emit-metadata="0"
data-input-position="bottom"
data-theme="preferred_color_scheme"
data-lang="en"
crossorigin="anonymous"
async
></script>
只需将其添加到网站的源代码中即可。如果你使用的是 AstroPaper 并希望在文章中启用评论,最可能的情况是导航到 PostDetails.astro 并将其粘贴到你希望评论出现的位置,也许是在 Share this post on: 按钮下方。
<Layout {...layoutProps}>
<main>
<ShareLinks />
<script
src="https://giscus.app/client.js"
data-repo="[ENTER REPO HERE]"
data-repo-id="[ENTER REPO ID HERE]"
data-category="[ENTER CATEGORY NAME HERE]"
data-category-id="[ENTER CATEGORY ID HERE]"></script>
</main>
<Footer />
</Layout>src/layouts/PostDetails.astro
这就完成了!你已成功在 AstroPaper 中集成评论!
支持浅色/深色主题的 React 组件
布局中嵌入的 script 标签是静态的,Giscus 配置(包括 theme)被硬编码在布局中。鉴于 AstroPaper 具有浅色/深色主题切换功能,如果评论能随网站其余部分在浅色和深色主题之间无缝切换那就太好了。为了实现这一点,需要一种更复杂的 Giscus 嵌入方法。
首先,我们要为 Giscus 安装 React 组件:
npm i @giscus/react && npx astro add react
然后在 src/components 中创建一个新的 Comments.tsx React 组件:
import Giscus, { type Theme } from "@giscus/react";
import { GISCUS } from "@/constants";
import { useEffect, useState } from "react";
interface CommentsProps {
lightTheme?: Theme;
darkTheme?: Theme;
}
export default function Comments({
lightTheme = "light",
darkTheme = "dark",
}: CommentsProps) {
const [theme, setTheme] = useState(() => {
const currentTheme = localStorage.getItem("theme");
const browserTheme = window.matchMedia("(prefers-color-scheme: dark)")
.matches
? "dark"
: "light";
return currentTheme || browserTheme;
});
useEffect(() => {
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
const handleChange = ({ matches }: MediaQueryListEvent) => {
setTheme(matches ? "dark" : "light");
};
mediaQuery.addEventListener("change", handleChange);
return () => mediaQuery.removeEventListener("change", handleChange);
}, []);
useEffect(() => {
const themeButton = document.querySelector("#theme-btn");
const handleClick = () => {
setTheme(prevTheme => (prevTheme === "dark" ? "light" : "dark"));
};
themeButton?.addEventListener("click", handleClick);
return () => themeButton?.removeEventListener("click", handleClick);
}, []);
return (
<div className="mt-8">
<Giscus theme={theme === "light" ? lightTheme : darkTheme} {...GISCUS} />
</div>
);
}src/components/Comments.tsx
这个 React 组件不仅包装了原生的 Giscus 组件,还引入了额外的 props,即 lightTheme 和 darkTheme。利用两个事件监听器,Giscus 评论将与网站的主题保持一致,每当网站或浏览器主题更改时,都会在深色和浅色主题之间动态切换。
我们还需要定义 GISCUS 配置,最佳位置是在 constants.ts 中:
import type { GiscusProps } from "@giscus/react";
...
export const GISCUS: GiscusProps = {
repo: "[ENTER REPO HERE]",
repoId: "[ENTER REPO ID HERE]",
category: "[ENTER CATEGORY NAME HERE]",
categoryId: "[ENTER CATEGORY ID HERE]",
mapping: "pathname",
reactionsEnabled: "0",
emitMetadata: "0",
inputPosition: "bottom",
lang: "en",
loading: "lazy",
};src/constants.ts
注意,在此处指定 theme 将覆盖 lightTheme 和 darkTheme props,导致静态主题设置,类似于以前使用 <script> 标签嵌入 Giscus 的方法。
为了完成此过程,将新的 Comments 组件添加到 PostDetails.astro(替换上一步中的 script 标签)。
import Comments from "@/components/Comments";
<ShareLinks />
<Comments client:only="react" />
<hr class="my-6 border-dashed" />
<Footer />src/layouts/PostDetails.astro
就是这样!