跳到主要内容

添加 giscus 评论功能

安装依赖

npm install --save @giscus/react

创建评论组件

src/components/Comment.tsx
import React from 'react'
import { useThemeConfig, useColorMode } from '@docusaurus/theme-common'
import Giscus, { GiscusProps } from '@giscus/react'
import { useLocation } from '@docusaurus/router';

const defaultConfig: Partial<GiscusProps> = {
id: 'comments',
mapping: 'specific',
reactionsEnabled: '1',
emitMetadata: '0',
inputPosition: 'top',
loading: 'lazy',
strict: '1', // 用根据路径标题自动生成的 sha1 值,精确匹配 github discussion,避免路径重叠(比如父和子路径)时评论加载串了
lang: 'zh-CN',
}

export default function Comment(): JSX.Element {
const themeConfig = useThemeConfig()

// merge default config
const giscus = { ...defaultConfig, ...themeConfig.giscus }

if (!giscus.repo || !giscus.repoId || !giscus.categoryId) {
throw new Error(
'You must provide `repo`, `repoId`, and `categoryId` to `themeConfig.giscus`.',
)
}

const path = useLocation().pathname.replace(/^\/|\/$/g, '');
const firstSlashIndex = path.indexOf('/');
var subPath: string = ""
if (firstSlashIndex !== -1) {
subPath = path.substring(firstSlashIndex + 1)
} else {
subPath = "index"
}

giscus.term = subPath
giscus.theme =
useColorMode().colorMode === 'dark' ? 'transparent_dark' : 'light'

return (
<Giscus {...giscus} />
)
}

是否需要支持隐藏评论?

在让页面支持评论功能之前,先确定下,是否需要支持隐藏评论。

因为需要通过 swizzling 的方式修改页面将评论功能嵌入进去。如果需要隐藏评论就需要通过 eject 的方式完全自定义页面以便拿到页面的 metadata 来判断是否要隐藏评论;而如果不需要隐藏评论,就只需要使用 wrap 的方式对页面进行跟轻量的修改。

两者都需要修改文档页面组件,有一定的侵入性,但 eject 方式相比 wrap 方式侵入性更大,在升级 docusaurus 的时候,前者更容易出现兼容性问题,如果出现不兼容就需要按照下文的方式重新 swizzle 并修改代码。

最佳实践是:如果不需要隐藏评论这种功能,就用 wrap 方式修改页面组件。

不支持隐藏的评论

本人就是使用的这种方式,因为没有隐藏评论的需求,也避免升级 docusaurus 时出现兼容性问题,下面介绍具体操作步骤。

文档页面启用评论

文档页面由 docusaurus 的 DocItem/Layout 组件渲染,下面是自定义方法。

  1. swizzle DocItem
npm run swizzle @docusaurus/theme-classic DocItem/Layout -- --wrap --typescript
  1. 修改以下自动生成的源码文件(高亮的行是增加的内容):
src/theme/DocItem/Layout/index.tsx
import React from 'react';
import Layout from '@theme-original/DocItem/Layout';
import type LayoutType from '@theme/DocItem/Layout';
import type { WrapperProps } from '@docusaurus/types';
import Comment from '@site/src/components/Comment';

type Props = WrapperProps<typeof LayoutType>;

export default function LayoutWrapper(props: Props): JSX.Element {
return (
<>
<Layout {...props} />
<Comment />
</>
);
}

文档目录页面启用评论

所谓文档目录页面就是自动生成的目录页面 (category),没有对应的 markdown 文件,这种页面是由 docusaurus 的 DocCategoryGeneratedIndexPage 渲染的,下面是自定义方法。

  1. swizzle DocCategoryGeneratedIndexPage:
npm run swizzle @docusaurus/theme-classic DocCategoryGeneratedIndexPage -- --wrap --typescript
  1. 修改以下自动生成的源码文件(高亮的行是增加的内容):
src/theme/DocCategoryGeneratedIndexPage/index.tsx
import React from 'react';
import DocCategoryGeneratedIndexPage from '@theme-original/DocCategoryGeneratedIndexPage';
import type DocCategoryGeneratedIndexPageType from '@theme/DocCategoryGeneratedIndexPage';
import type { WrapperProps } from '@docusaurus/types';
import Comment from '@site/src/components/Comment';

type Props = WrapperProps<typeof DocCategoryGeneratedIndexPageType>;

export default function DocCategoryGeneratedIndexPageWrapper(props: Props): JSX.Element {
return (
<>
<DocCategoryGeneratedIndexPage {...props} />
<Comment />
</>
);
}

支持隐藏的评论

如果有隐藏评论的需求,就需要对页面做更深度的自定义,使用 eject 方式将页面组件源码弹出到 theme 下,基于源码进行修改。

文档页面支持评论

  1. swizzle DocItem:
npm run swizzle @docusaurus/theme-classic DocItem/Layout -- --eject --typescript
  1. 修改以下自动生成的源码文件(高亮的行是增加的内容):
src/theme/DocItem/Layout/index.tsx
import React from 'react';
import clsx from 'clsx';
import { useWindowSize } from '@docusaurus/theme-common';
import { useDoc } from '@docusaurus/theme-common/internal';
import DocItemPaginator from '@theme/DocItem/Paginator';
import DocVersionBanner from '@theme/DocVersionBanner';
import DocVersionBadge from '@theme/DocVersionBadge';
import DocItemFooter from '@theme/DocItem/Footer';
import DocItemTOCMobile from '@theme/DocItem/TOC/Mobile';
import DocItemTOCDesktop from '@theme/DocItem/TOC/Desktop';
import DocItemContent from '@theme/DocItem/Content';
import DocBreadcrumbs from '@theme/DocBreadcrumbs';
import Unlisted from '@theme/Unlisted';
import type { Props } from '@theme/DocItem/Layout';

import styles from './styles.module.css';
import Comment from '../../../components/Comment';

/**
* Decide if the toc should be rendered, on mobile or desktop viewports
*/
function useDocTOC() {
const { frontMatter, toc } = useDoc();
const windowSize = useWindowSize();

const hidden = frontMatter.hide_table_of_contents;
const canRender = !hidden && toc.length > 0;

const mobile = canRender ? <DocItemTOCMobile /> : undefined;

const desktop =
canRender && (windowSize === 'desktop' || windowSize === 'ssr') ? (
<DocItemTOCDesktop />
) : undefined;

return {
hidden,
mobile,
desktop,
};
}

export default function DocItemLayout({ children }: Props): JSX.Element {
const docTOC = useDocTOC();
const { frontMatter } = useDoc();
const { hide_comment: hideComment } = frontMatter;
const {
metadata: { unlisted },
} = useDoc();
return (
<div className="row">
<div className={clsx('col', !docTOC.hidden && styles.docItemCol)}>
{unlisted && <Unlisted />}
<DocVersionBanner />
<div className={styles.docItemContainer}>
<article>
<DocBreadcrumbs />
<DocVersionBadge />
{docTOC.mobile}
<DocItemContent>{children}</DocItemContent>
<DocItemFooter />
</article>
<DocItemPaginator />
</div>
{!hideComment && <Comment />}
</div>
{docTOC.desktop && <div className="col col--3">{docTOC.desktop}</div>}
</div>
);
}

大功告成!

对于不需要启用评论的文章,在 markdown 文件前面加上 hide_comment: true 即可,示例:

intro.md
---
hide_comment: true
---

其它页面支持评论

对于文档目录页面如何判断是否隐藏评论,思路与文档页面类似,拿到目录(category)相关的 metadata,判断 hide_comment 的值来决定是否隐藏评论;对于首页,如果需要评论,就固定 import 并在文末使用 <Comment /> 标签即可,如果不需要则不需要做什么。

但本文没用此方案,所以也没有深入研究,有需要的可参考这里说的思路进行实现。

配置 giscus

Giscus 的评论数据是存放在 GitHub 仓库的 Discussions 中,所以先准备一个 GitHub 仓库(如果是 docusaurus 构建的开源站点,通常用存放网站源码仓库),在设置里启用 Discussions:

然后进入 giscus 官网网站,输入 GitHub 仓库和 Discussion 分类(通常用 General):

下面会自动生成 giscus 配置所需的关系 ID 信息:

将其贴在 docusaurus.config.ts 配置文件中:

  themeConfig:
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
({
// giscus 评论功能
giscus: {
repo: '***************',
repoId: '************',
category: 'General',
categoryId: '********************',
},

然后就会自动启用评论了。

评论数据迁移

更换文档路径

如果文档的路径更换了,需要同步修改下对应 Discussion 中的 SHA1 Hash 值(可使用 SHA-1 在线工具进行计算,比如 SHA-1 hash calculator):

当然对应的 URL 和路径标题最好也同步改下,但这个在精确匹配的模式下不是必须的。

更换 GitHub 仓库

有时候需要将文档迁移到新的 GitHub 仓库,比如内容积累太多,需要将部分内容拆分到其它的仓库中。

这时可以将已有的评论对应的 Discussion 迁移到新的仓库中:

记得同步修改下新的 SHA1 Hash。