惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
Docker
Microsoft Azure Blog
Microsoft Azure Blog
云风的 BLOG
云风的 BLOG
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
L
LangChain Blog
P
Privacy & Cybersecurity Law Blog
Hugging Face - Blog
Hugging Face - Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
大猫的无限游戏
大猫的无限游戏
Cyberwarzone
Cyberwarzone
The Register - Security
The Register - Security
Stack Overflow Blog
Stack Overflow Blog
A
Arctic Wolf
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
T
Threatpost
The GitHub Blog
The GitHub Blog
P
Privacy International News Feed
WordPress大学
WordPress大学
U
Unit 42
S
Securelist
T
The Exploit Database - CXSecurity.com
C
Cyber Attacks, Cyber Crime and Cyber Security
P
Proofpoint News Feed
Latest news
Latest news
Hacker News: Ask HN
Hacker News: Ask HN
小众软件
小众软件
Know Your Adversary
Know Your Adversary
The Cloudflare Blog
V
Vulnerabilities – Threatpost
The Hacker News
The Hacker News
Scott Helme
Scott Helme
有赞技术团队
有赞技术团队
Security Latest
Security Latest
Google DeepMind News
Google DeepMind News
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Simon Willison's Weblog
Simon Willison's Weblog
博客园 - Franky
Y
Y Combinator Blog
博客园 - 叶小钗
Security Archives - TechRepublic
Security Archives - TechRepublic
Google DeepMind News
Google DeepMind News
N
Netflix TechBlog - Medium
S
Secure Thoughts
T
Threat Research - Cisco Blogs
aimingoo的专栏
aimingoo的专栏
S
SegmentFault 最新的问题
Microsoft Security Blog
Microsoft Security Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
博客园 - 司徒正美
M
MIT News - Artificial intelligence

HaHack

2020 蒙太奇(上) 让你的角色学会说话——rhubarb-lip-sync-ccc 2019 蒙太奇 脑机接口概述及Muse头环Python开发基础 小目标达成:一万元开源项目善款筹集计划 Cocos Creator 最佳实践之注意 JavaScript API 兼容性 wukong-robot:一个更加优雅的中文智能音箱项目 2018 蒙太奇 使用 BeamerStyleSlides 制作你的技术答辩 PPT 公开课:使用Cocos Creator开发微信小游戏《2048》 基于 Cocos 的高性能跨平台开发方案 0行代码实现叮当的场景联动 我在平安的两年 使用叮当声控智米电风扇 手把手教你编写叮当机器人插件 叮当:一个开源的中文智能音箱项目 化繁为简的企业级 Git 管理实战(五):二进制大文件的版本控制 化繁为简的企业级 Git 管理实战(四):多 Gitlab 数据同步 机器学习的数学基础:线性代数进阶篇
Comment.js:一个纯JS实现的静态站点评论系统
wzpan · 2017-07-02 · via HaHack

介绍我用纯JS实现的一个静态站点评论系统,以及实现过程中的心得体会。

前言

我的博客最早是使用 Disqus 来实现评论功能的。Disqus 被墙了之后,改成了多说。今年年初,多说也正式关闭了,于是我被逼着又开始寻找其他的替代评论系统。

我先是试用了网易云跟贴、畅言等几种类似的社会化评论系统。畅言要求站点必须备案,而我实在没有为了评论去申请备案的动力。网易云跟贴的管理后台上有很多不明觉厉的功能,但好像都没多大用处。最致命的问题是我不小心把我的站点绑定到了另一个网易账户,而不是我常用的微博账户。这样的话,我每次回贴就得退登到微博账户,要管理贴子的时候又得切回管理员账户,非常不方便。然而网易云跟贴并没有提供解绑的功能。于是我给他们提了需求,然而一直到现在都没有回复。再加上有了多说作为前车之鉴,我对国内的免费评论服务已经失去了信心。今天把A换成B,难以保证日后B也关闭了,被逼着又换到C,实在是懒得折腾下去啊。于是,我放弃了换用类似的评论系统的念头。

之后我找到了 isso 项目,它是一个 Python 实现的开源评论服务。这个服务需要搭建在自己的服务器上。官方的简介简明扼要:“a Disqus alternative”。出于对 Python 的好感,我把站点的评论功能迁移到了 isso 。然而,我对 isso 也并不是很满意。首先它的功能其实也非常弱,不支持 Markdown 语法,不支持 Gravatar 头像,也没有一个像样的管理后台,搭建和配置的过程也比较费时,远达不到开箱即用的程度。再加上 isso 需要服务器运营,为了一个评论系统而去购买服务器确实太奢侈了。用了几个月后,我又萌生了换掉它的念头。

项目介绍

我的想法来源于一些基于 Github issue 的博客。其实 Github 的 issue 本身就是一个非常完善的评论系统,有完善的管理后台,灵活的通知设置,而且 Github 是开放 API 的。只要我能把 Github 的 issue 与博客的页面打通,把 issue 上的内容显示在我的博客上,然后在需要评论的时候点击跳转到 Github 的 issue 页,就实现了一个基本可用的评论系统了。

comment.js 就是基于这个想法实现的一个评论系统,它的核心代码只有 400 行左右,却能够用来实现评论会话和最新评论列表的两个功能。比起已有的社会化评论系统,它有如下几个优点:

  1. 完善的评论管理系统。基于 issue 的评论,支持 Markdown ,支持 Gravatar。
  2. 开箱即用的邮件通知功能。Github 的邮件通知功能非常完善,不像 isso 那样还得配置邮件通知服务。
  3. 无需搭建后台。直接用现成的 issue 作为后端,不像 isso 那样还需要自己搭个后台,搞定数据库。
  4. 接入简单。获取评论会话和获取最新列表各自对应一个函数。
  5. 代码简单。这意味着你也可以很快上手脚本代码,对这个脚本进行定制。
  6. 除了 Github issue 之外,comment.js 也支持使用 OSChina issue 作为后端[1],即使 Github 被墙,也能通过修改参数迅速切换到其他备选站点,比起说关闭就关闭的评论服务可靠多了。

接入方法

comment.js 依赖几个 JS 前端库:

  • jQuery - 用于 Ajax 请求以及将评论内容插入到页面中。
  • marked - Markdown 支持。
  • timeago.js - 时间文本格式化。
  • spin - 用于在加载评论数据前先绘制一个 loading 动画(可选)。
  • highlight.js - 用于代码高亮(可选)。

0. 添加静态资源文件

在页面中添加这些资源:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

<link rel="stylesheet" href="path_to_comment_css/comment.css">








<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/0.3.6/marked.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/timeago.js/3.0.2/timeago.min.js"></script>


<script src="https://cdnjs.cloudflare.com/ajax/libs/spin.js/2.3.2/spin.min.js"></script>


<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
<script type="text/javascript">
marked.setOptions({
highlight: function (code, lang) {
return hljs.highlightAuto(code).value;
}
});
function Highlighting(){
var markdowns = document.getElementsByClassName('markdown');
for(var i=0;i<markdowns.length;i++){
if(markdowns[i].innerHTML) markdowns[i].innerHTML =marked(markdowns[i].innerHTML);
}
}
window.addEventListener('DOMContentLoaded', Highlighting, false);
window.addEventListener('load', Highlighting, false);
</script>


<script src="path_to_comment_js/comment.js"></script>

1. 注册 OAuth App

为了避免 API 被恶意滥用,Github API (以及 OSChina API)设定了一个API调用频率限制。为了提高频率限额,建议 [注册一个 Oauth App](Register a OAuth application](https://github.com/settings/applications/new)。

完成注册后,你将得到一个 client id 以及一个 client_secret ,先将这两个值记下来,后面我们会用到。

(提示:注册 App 的时候你可能会对 Authorization callback URL 这一项目感到困惑,一般填写你的站点地址即可。例如 http://hahack.com

2. 获取评论会话

第一步,在页面中添加一个 DIV ,用于展示评论会话内容。

1
<div id="comment-thread"></div>

第二步(可选),如果希望在加载完数据前先展示一个loading动画,还可以添加一个用于动画的 DIV :

1
<div id="loading-spin"></div>

最后,调用 getComments() 方法,获取该页面对应的 issue 包含的所有评论,然后展示到我们指定的 DIV 中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script type="text/javascript">
var opt = {
type: "github",
user: "wzpan",
repo: "comment.js",
no_comment: "No comments yet. Press the button and go to comment now!",
go_to_comment: "Go to comment",
issue_id: "1",
btn_class: "btn",
comments_target: "#comment-thread",
loading_target: "#loading-spin",
client_id: "xxxxxx",
client_secret: "xxxxxx"
};
getComments(opt);
</script>

参数说明:

  • type: 要作为后端的站点。目前支持 GithubOSChina
  • user: 您的 Github 用户名。
  • repo: 您用作评论后端的仓库名。
  • no_comment: 当没有评论时,展示的提示消息。
  • go_to_comment: “去留言” 按钮的按钮文本。
  • issue_title: 您当前页面对应的 issue 标题。也可以使用 issue_id ,二者只选其一。
  • issue_id: 您当前页面对应的 issue id。也可以使用 issue_title,二者只选其一。
  • btn_class: “去留言”按钮的 CSS 样式名。
  • comments_target: 用于展示评论内容的容器。例如我们上面所写的 comment-thread DIV 。
  • loading_target(可选):用于展示 loading 动画的容器。例如我们上面所写的 loading-spin DIV 。
  • client_id(可选但建议):您注册的 OAuth App 的 client id。
  • client_secret(可选但建议):您注册的 OAuth App 的 client secret。

效果参见本页面下方的留言区。

3. 获取最新评论列表

评论列表用于获取你最近的若干条评论,效果可以参见 站点首页 右侧的最新留言区。

要获取最新评论列表的方法也大同小异。首先写一个 DIV 用于加载获取得到的评论列表数据:

1
<div id="recent-comments"></div>

之后可以调用 getRecentCommentsList() 方法,获取最近评论列表并展示到指定的 DIV 中。

1
2
3
4
5
6
7
8
9
10
11
12
<script type="text/javascript">
var opt = {
type: "github",
user: "wzpan",
repo: "comment.js",
recent_comments_target: "#recent-comments",
count: 5,
client_id: "xxxxxx",
client_secret: "xxxxxx"
};
getRecentCommentsList(opt);
</script>

参数说明:

  • type: 要作为后端的站点。目前支持 GithubOSChina
  • user: 您的 Github 用户名。
  • repo: 您用作评论后端的仓库名。
  • recent_comments_target: 用于展示最新评论列表的容器。例如我们上面所写的 recent-comments DIV 。
  • count: 列表的最大长度。
  • client_id(可选但建议):您注册的 OAuth App 的 client id。
  • client_secret(可选但建议):您注册的 OAuth App 的 client secret。

开发心得

下面照例总结下项目的开发心得。虽然整个项目只有几百行的代码,但这个过程中还是不可避免的遇到一些困难。

关于选型和项目命名

一开始的想法只是给 Hexo 写一个插件,让其能够实现评论功能。最理想的情况是类似 hexo-generator-search 那样,npm install 一下,然后 _config.yml 里添加下配置就完事。通过阅读 Hexo 的文档后我发现 helper 似乎比较适合用作这个目的:把核心功能写成一个 helper ,然后在模板文件里直接执行这个 helper ,得到的数据还能进一步再模板中调诸如 markdown 等其他现成的 helper, 这样还能实现 Markdown 支持。于是我最初的项目仓库名叫做 hexo-helper-github-comment 。

等我实现了 getComments() 方法后,我发现我的想法是错误的:helper 只适用于同步执行的操作,不适合网络请求这种异步操作。这带来的问题就是模板文件里已经成功执行了 helper 了,也返回了数据,但此时 renderer 早已经完成了模板的渲染了,而异步返回的评论数据却不再能够被渲染。

之后我想在 NodeJS 中加入 jQuery,用 jQuery 来操纵 DOM ,而不再依赖 renderer 。但这个方案似乎也不可行。因为在模板文件中,DOM 还没有创建,jQuery 拿不到实际的 DOM 。

所以最终我改成了纯 JS 的方案,把请求的方式也从 request-promise 改成了 AJAX ,然后在模板文件中直接跑 JS ,让 JS 完成请求,此时的 DOM 是已创建的,可以使用 jQuery 来操纵页面。虽然这样做就不能直接用 Hexo 现成的 markdown helper 了,但由于是纯 JS 实现,这个库也就可以在任何静态站点中使用,变得更加通用了。于是我把仓库名改成了 github-comment 。

又后来,我准备开源的前一天,在微博上先公开了关于这个项目的信息。有些人也表示了 Github 将来也可能被墙的质疑。于是我花了几分钟时间,也加入了对 OSChina 的支持。这个仓库名似乎也不只是基于 Github 了,于是我又把仓库名改成了 comment.js 。

关于取舍

我最纠结的部分,在于要不要把评论框也写进来。

直接在页面中写评论,减少了页面的跳数,当然是一大收益。但这样做也有几个问题:

  1. 功能可用性和项目的复杂度的取舍。Github 的编辑框其实包含了非常多的功能,例如支持拖拽的附件添加、表情、预览、快捷键等等,如果不把这些功能加进来,编辑框的功能就显得很鸡肋,远不如在 Github 中评论有趣;如果加进来,整个项目的代码就远不止 400 行这么简单了。
  2. 通用性和专用程度的取舍。为了避免 Github 单点问题,comment.js 还支持 OSChina 作为备选评论系统。加入 Github 的这些编辑功能,是否会影响对其他站点后端的兼容性又是个问题。
  3. 界面美观程度和版权的取舍。现在的评论会话界面几乎照搬了 Github 的样式,因为点击“去留言”按钮实际上直接跳到了 Github ,相当于为 Github 做了引流,给了一个大大的版权说明,也就没有了侵权的担忧。如果界面完全隔离了 Github,也隐藏了 Github 的版权信息,反而有点滥用平台的感觉[2]

有意思的是,当我刚发布 comment.js 的时候,我才发现几个月前已经有人做了一个类似的项目:gitment,真是心有灵犀啊。这个项目与我的项目的最大区别就在于它实现了内置的编辑框,并且目前只支持 Github 。如果你认为评论框必不可少,那么建议使用 gitment;反之如果你觉得点击按钮跳到 Github 页面似乎也还能接受,担心 Github 单点问题,而且觉得保证代码的简单和通用性更重要的话,那么不妨使用 comment.js 。


  1. 目前 OSChina 的 API 在浏览器端会出现 CORS 错误。我已经给 OSChina 提交了工单,待后台添加 CORS 支持后就可以使用 OSChina 作为后端。 ↩︎

  2. 话说回来直接照搬界面确实不太好,后面我会对评论会话的UI进行调整,避免侵权。 ↩︎