
这是悟空的第 111 篇原创文章
本文主要内容如下:

我的开源项目 PassJava 有个在线的技术文档,但是没有评论功能,总感觉缺了点什么,这次来给它加上留言功能。
最后留言的效果图如下:

文档演示地址:http://www.passjava.cn
之前做了一个介绍 PassJava 的短视频,
一款基于 Spring Cloud 的面试刷题系统
首先我的这个文档网站是基于开源的 docsify 的:
docsify 是一个动态生成文档网站的工具。不同于 GitBook、Hexo 的地方是它不会生成将
.md转成.html文件,所有转换工作都是在运行时进行。
如果只是需要快速的搭建一个小型的文档网站,或者不想因为生成的一堆 .html 文件“污染” commit 记录,只需要创建一个 index.html 就可以开始写文档而且直接部署在 GitHub Pages。

开源项目 PassJava 地址:https://github.com/Jackson0714/PassJava-Platform,欢迎 Star。 本文已收录至:www.passjava.cn
那如何给 docsify 添加评论呢?这里就是要用到 Gitalk 了。
Gitalk 是一个基于 GitHub Issue 和 Preact 开发的
评论插件。
扩展知识:Preact 是 React 的 3KB 轻量级替代方案,它拥有着和 React 一样的 API。React 用于构建用户界面的 JavaScript 库。
Gitalk 嵌入到个人网站中,然后利用了 Github 的 Issue 功能,把 Github 的 Issue 中的 Comments 当作某篇文章的评论。当然,这些功能都是 Gitalk 自带的,我们不用关心,这里我还是剖析下 Gitalk 的原理。

Github 的 Issue 功能,可能大家不知道,可以理解为贴吧的帖子,我截个图大家就懂了。在 Github 的开源项目中,Issue Tab 下可以提出问题,也可以在里面加 comments(评论)。

首先创建评论时 Gitalk 会调用 Github API 在 Github 的 Issue 中添加 Comments。我们也可以到 Github 的 Issue 中查看评论或者添加评论。
添加评论的 API:
https://api.github.com/repos/Jackson0714/PassJava-Learning/17/comments
请求的参数:
{
body: "有什么问题吗?"
}
如下图所示:

当我们打开网站查看评论列表时,Gitalk 会根据仓库名、标签 获取 Github 上 Issue 的评论列表。
获取评论列表请求的 API:
https://api.github.com/repos/Jackson0714/PassJava-Learning/issues
如下图所示:

仓库名:PassJava-Learning
标签:Gitalk、02.PassJava架构篇/24.缓存实战(四)SpringCache。
仓库名是配置出来的,标签是 Gitalk 自动获取的。
Gitalk 要想使用 Github 的 Issue 功能,则需要在 Github 上创建一个授权应用,拿到应用的 id 和 密钥配置到 Gitalk 脚本中就可以了。
Gitalk 提供了评论功能的 JavaScript 脚本和评论的样式,直接在网站中引入即可。后面会有详细的配置方法。
Gitalk 是借助 Github 的仓库的 Issues 功能的,所以我们需要在 Github 上配置授权应用(GitHub OAuth application),让自己的网站能够通过这个授权应用将评论放到 Issues 上。
创建 GitHub OAuth application流程:
1、打开github网站登陆后,点击右上角的用户图标,选择Settings。
2、 在Settings页面选择Developer settings选项。
3、在Developer settings选择OAuth Apps,然后会在页面右边有一个New OAuth App按钮,点击这个按钮就进入到新建OAuth application页面。
4、也可以直接代开这个链接:进入新建页面。
https://github.com/settings/applications/new
如下图所示:

接着填写应用的基本信息:App name,任意填写,Homepage URL 和 Callback URL 填写网站的域名,两个地方的域名保持一致,如下所示:

点击 Register Application 就可以创建成功了,会生成应用的 id 和密钥,如下图所示。这两个信息在配置 gitalk 的时候用到,非常重要。而且 secret 你要第一时间备份下,后面再进来就是隐藏的了,除非重新生成一个新的。

官方的使用方式很简单,直接在自己的网站中加入 Gitalk 的脚本库文件和 css 文件
<link rel="stylesheet" href="//cdn.bootcss.com/gitalk/1.5.0/gitalk.min.css">
<script src="//cdn.bootcss.com/gitalk/1.5.0/gitalk.min.js"></script>
在 html 文件中添加一个容器,Gitalk组件会在此处显示
<div id="gitalk-container"></div>
然后使用下面的 JavaScript 代来生成 Gitalk 评论
var gitalk = new Gitalk({
clientID: '7de8e380bec2231f0544', // GitHub Application Client ID
clientSecret: 'xxxx', // GitHub Application Client Secret
repo: 'PassJava-Learning' // 存放评论的仓库
owner: 'Jackson0714', // 仓库的创建者,
admin: ['Jackson0714'], // 如果仓库有多个人可以操作,那么在这里以数组形式写出
id: location.pathname, // 用于标记评论是哪个页面的,确保唯一,并且长度小于50
})
gitalk.render('gitalk-container'); // 渲染Gitalk评论组件
更多参数介绍详见本文附录。
Gitalk 官方提供的使用脚本对于我用 docsify 搭建的网站有些不足之处,所以我就动手自己改了。
首先我的网站每个页面的标题都携带了中文,比如这个:
http://www.passjava.cn/#/94.Git/01.Git常见问题
如果评论这篇文章,就会在我的 PassJava-Learning 仓库的 issues 中生成一个 url 编码后的标题。
就像下面这样,没人看得懂吧,所以需要在 gitalk 的脚本中解码 url 。

加一行解码的代码搞定:
decodeURI(title)
如果你需要到 Github 上更方便地维护留言记录,就可以应用上面的代码了。
因为 docsify 是用 # 符号来表示每篇文章的 url 的,我想把 # 符号后面的取出来。比如下面这个:
http://www.passjava.cn/#/02.PassJava架构篇/22.缓存实战(二)Redis分布式锁
我只想要 02.PassJava架构篇/22.缓存实战(二)Redis分布式锁 作为标题。改造如下:
title = location.hash.match(/#(.*?)([?]|$)/)
if (title != null) {
title = location.hash.match(/#(.*?)([?]|$)/)[1]
}
title = decodeURI(title.substring(1, title.length))
id 和 title 我都是用的 URL 中 # 后的标题。有时候标题太长了,导致发起评论和加载评论。页面会报 Github 的 api 问题。
所以我就把 id 和 title 限制在 50 个字符以内,如果超出了就用 home page作为 id 和 title。
// 限制 50 个字符
if (title != null) {
title = decodeURI(title.substring(1, title.length))
if (title.length >= 50) {
title = title.substring(title.length - 50, title.length)
}
} else {
title = 'home page'
}
经过上面的改造后,Gitlab 中 issue 的列表就是下面这样了

大家可以看到有两个标签,一个是 Gitalk,一个是 url 标题,Gitalk 就是通过这两个标签来获取评论列表的,我们可以点一个 issue 进去看下:

因为 Gitalk 是基于 Gitlab 的 Issue 功能,所以我们可以直接在 issue 里面加评论的,博客上会同步显示这些评论哦。
注意:千万别点 close issue 按钮,关闭 issue 后,评论就都看不到了,而且即使你再 reopen issue,也不行。只能重新在博客评论,但是会在 Github 上自动新建一个 issue,不能和之前的评论关联起来,有点坑呀。。
由于docsify的链接 URL 使用的是 hash 的方式,导致切换页面的时候不会刷新页面,导致整个网站的Gitalk评论使用的是一个评论,因此做了监听 hash 事件,来刷新页面,这样就能每次切换页面刷新,然后加载对应的评论。
window.onhashchange = function(event) {
if(event.newURL.split('?')[0] !== event.oldURL .split('?')[0]) {
location.reload()
}
}
经过改造的代码,在公众号悟空聊架构回复 博客获取。
在使用 Gitalk 也遇到了一些奇怪的问题,这里做个记录:
这个问题是我的好朋友 飞羽 提出的。
如果你在某篇文章中评论了, 如果有其他人跟帖,你也会收到 Gitlab 的邮件提醒哦,类似朋友圈功能。

如果你想取消掉,直接在你评论的 issue 里面取消订阅就可以了。如下图所示:


用 F12 调试工具看了后,是因为自己写的 JS 报错了。
这是因为我最开始创建的Github 应用是 Github App 而不是 OAuth App,这里大家注意下。
需要在切换文章时,重新给 location 变量赋值,详见本文 5.4 的改进代码。
id 太长了,修复代码详见本文 5.3。
经过改造的代码,在公众号悟空聊架构回复 博客获取。
OAuth App的Client IDOAuth App的Client Secret注意:
虽然id和title参数是不是必填的选项,但是这个两个参数很重要建议填上:
1、id参数用于评论记录和页面对应的唯一标记,有的时候发现好几个页面评论是一样的,就是由于配置id参数的时候,这几个页面的id是一样导致。由于id参数默认值是location.href页面URL,而有的时候URL中带着页面标题的链接,导致URL长度超过了50个字符长度,会导致创建评论issues失败(长度超过50个会创建失败),这点要注意。
2、title用于在 Github 仓库issues的标题,如果你想管理评论可以设置一下这个参数,来区分该评论是来自于那个文章。
number: 类型:数字,选填,页面的 issue ID 标识,若未定义number属性则会使用id进行定位。默认值:-1。
labels:类型:数组,选填,GitHub issue 的标签,默认值:Gitalk
body:类型:字符串,选填, GitHub issue 的内容,默认值:URL + HTML中meta标签中description的值。
language:类型:字符串,选填,设置语言,支持 [en, zh-CN, zh-TW]。默认值:navigator.language 或者 navigator.userLanguage。
perPage:类型:数字,选填,每次加载的数据大小,最多 100。默认值:10。
distractionFreeMode:类型:布尔值,选填,类似Facebook评论框的全屏遮罩效果。默认值:false。
pagerDirection:类型:字符串,选填,评论排序方式,last为按评论创建时间倒叙,first为按创建时间正序。默认值:last。
createIssueManually:类型:布尔值,选填,如果当前页面没有相应的 isssue 且登录的用户属于 admin,则会自动创建 issue。如果设置为 true,则显示一个初始化页面,创建 issue 需要点击 init 按钮。默认值:false。
proxy:类型:字符串,选填,GitHub oauth 请求到反向代理,为了支持 CORS。默认值:https://cors-anywhere.herokuapp.com/https://github.com/login/oauth/access_token。
flipMoveOptions:类型:对象,选填,评论列表的动画。参考 react-flip-move
enableHotKey:类型:布尔值,选填,启用快捷键(cmd/ctrl + enter)提交评论。默认值:true。
参考资料:
https://segmentfault.com/a/1190000018072952
https://github.com/gitalk/gitalk
开源项目 PassJava 地址:https://github.com/Jackson0714/PassJava-Platform,欢迎 Star。 本文已收录至:www.passjava.cn

- END -