Hexo博客搭建(3)——优化评论系统

温馨提示:点击页面下方以展开或折叠目录

摘要:博客采用Valine评论系统,其基于LeanCloud,配置较为繁琐,故单独取一篇幅记录其配置过程。

Hexo博客搭建
博客系列:Hexo博客搭建(1)——建站及部署,环境搭建并部署至Github。
博客系列:Hexo博客搭建(2)——主题配置,针对pure主题进行个性化配置。
博客系列:Hexo博客搭建(3)——优化评论系统,主要对Valine评论系统进行优化。
博客系列:Hexo博客搭建(4)——常见问题,修复了pure主题的系列小bug。
文章作者:鴻塵

1.前言

Hexo支持disqusyouyanliveregitmentgitalkvaline等多种评论系统,此部分仅用于Valine评论系统。
原版的配置仅能记录评论,不能实现邮件提醒QQ头像等个性化需求。
头像倒无所谓,Valine自带的头像样式(monsteridwavatarrobohash)其实也差不多够用了,但远不如QQ头像的辨识度高。最主要的是邮件提醒功能,比如讨论某个问题,当有了答案的时候却无法通知对方,因为别人不可能一直守着你的网站等回复吧,所以「邮件提醒」就显得极其重要且必要了。
基础配置参考前文Hexo博客搭建(2)——主题配置之评论系统,下文是进阶配置

进阶配置主要参考:
Valine评论之Valine-admin配置攻略
pure主题valine留言板添加:昵称、邮箱必填必写功能


2.部署valine-admin

2.1.创建应用

注册登录 LeanCloud 的官网,推荐使用「国际版」,因为附赠了一个「LeanCloud评论管理」的域名https://xxx.avosapps.us
创建应用,选择免费的「开发板」即可,创建完成如图所示。
创建LeanCloud应用

2.2.部署项目

⑴进入创建好的应用,依次点击「云引擎」→「部署」→「项目部署」,如图所示。
部署项目
⑵此时默认是在「命令行工具部署」的教程页面,切换到「Git部署」,点击「配置Git」,在「Git remote URL」填入https://github.com/sviptzk/Valine-Admin-Server,如图所示。
部署项目
⑶点击「部署项目」,回到「Git部署」选项卡,可以看到「Deploy fromhttps://github.com/sviptzk/Valine-Admin-Server」,「手动部署」下点击「部署」即可,如图所示。
部署项目
⑷等待部署完成即可,控制台打印部署日志如图。
部署项目
⑸部署过程完整动图如下,图片来源
部署项目动图演示

2.3.绑定评论管理的域名

说明:这一步非必须,评论管理模块可以直接在「存储」→「结构化数据」→「Comment」处操作,但是「国际版域名」对应的评论后台管理系统可以对发送邮件提醒失败的评论重新发送通知、可以标记垃圾邮件,综上还是有必要配置这一步的。

由于LeanCloud分为「国区版」和「国际版」,只有「国际版」才分配一个免费的二级域名,上文也推荐使用「国际版」,因此默认已有该域名:https://xxx.avosapps.us

获取国际版域名:依次点击「云引擎」→「设置」→「云引擎域名」,设置自己的独立域名,例如:hwame.avosapps.us,如图所示。

绑定评论管理的域名

2.4.设置域名白名单

如果没有设置域名白名单,网页就无法加载评论,显示权限错误。
每行一个域名,多个域名换行分隔。子域名需每条列出,不支持通配符。协议、域名和端口号都需严格一致(如果使用的是默认端口号可以省略,但协议头一定要写)。例如我的设置如图:
域名白名单

2.5.设置环境变量

依次点击「云引擎」→「设置」→「自定义环境变量」,添加环境变量,此步骤非常重要,设置时务必弄清楚每一个变量的含义。
下表仅为本人所设置的环境变量(按字母排序),根据需要自行增减,其他环境变量参考Valine评论之Valine-admin配置攻略

变量名 必需 说明 示例
ADMIN_URL 可选 *评论后台的管理地址(非博客地址 https://hwame.avosapps.us/
BLOGGER_EMAIL 可选 *评论后台的管理员邮箱,默认使用SMTP_USER xxx@qq.com
SENDER_NAME 必填 邮件寄件人名称 鴻塵
SITE_NAME 必填 网站名称 鴻塵的博客
SITE_URL 必填 网站地址,最后不要加/ https://hwame.top
SMTP_PASS 必填 *SMTP授权码 abcdefghijklmnop
SMTP_SERVICE 必填 *邮件服务提供商 QQ
SMTP_USER 必填 SMTP服务用户名,一般为邮箱地址 xxx@qq.com
TEMPLATE_NAME 必填 *提醒邮件的主题 rainbow
TO_EMAIL 可选 博主通知收件地址,默认使用SMTP_USER xxx@qq.com

*号字段的详细说明:

  • ADMIN_URL:评论后台的管理地址,即上文2.3.绑定评论管理的域名设置的域名。通过该后台可以对发送邮件提醒失败的评论重新发送通知、可以标记垃圾邮件,而「结构化数据」中的「Comment」可以修改评论内容/昵称/时间为评论添加QQ头像等,两处都可以查看邮件提醒是否成功
  • BLOGGER_EMAIL:评论后台的管理员邮箱,如果设置则作为后台管理员邮箱(/sign-up页面设置),如果不设置则默认为SMTP_USER
  • SMTP_PASS:SMTP授权码,注意不是邮箱的登陆密码,QQ以外的请自行查询对应邮件服务商的获取方式,以QQ邮箱为例(需要获取QQ邮箱独立密码):
    • 登陆QQ邮箱,点击「设置」→「账户」,下滑至「POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务」,开启「POP3/SMTP」和「IMAP/SMTP」服务,如图所示:
      SMTP授权码
    • 点击上图中的「生成授权码」,根据提示发送手机短信,完成后将生成16位字符的授权码,出于安全考虑,该授权码仅显示一次。
    • 将授权码填入环境变量SMTP_PASS字段。
  • SMTP_SERVICE:邮件服务提供商,一般都是使用QQ邮箱,其他常用邮箱类型(如126163GmailHotmailOutlook365Yahoo等)操作类似,可用选项参考Supported services
  • TEMPLATE_NAME:评论提醒邮件的主题模板,可选值有defaultrainbow(效果如图),custom1custom2,简洁风可直接使用defaultrainbow,若使用后两者,可能还需要设置附加环境变量,具体参考部署valine-admin
    rainbow风格评论提醒效果

2.6.重新部署

环境变量设置完成后需要重新部署实例,使其生效。
注意:由于每个人的需求不同,配置时务必弄清楚各变量的含义。
注意:每次修改或添加环境变量后,都需要重启/重新部署实例,否则配置不生效。

2.7.初始化后台管理

注意:如果你没有设置后台域名(即上文2.3.绑定评论管理的域名设置的域名),那么也可以跳过这一步。

  • 初始化
    设置登陆信息,打开https://domain/sign-up,其中domain为绑定的后台管理的域名,例如使用「国际版」赠送的域名https://hwame.avosapps.us/,那么我访问的地址就是 https://hwame.avosapps.us/sign-up
  • 后台登陆
    设置登陆信息后就可以用登录名登录了,登陆后台直接使用https://hwame.avosapps.us/即可,若使用https://hwame.avosapps.us/sign-up可能会有bug。

2.8.基本配置完成

到这里基本上完成了valine-admin的基本设置。


3.防止服务器休眠

3.1.创建定时任务

经过上述设置,基本上能满足我们的日常要求。

此部分参考:
Valine评论系统详解之防止服务器休眠
Valine-admin配置攻略之防止服务器休眠

目前最新版的Valine Admin已经可以实现自唤醒,即在 LeanCloud 云引擎中定时请求 Web 应用地址防止休眠。对于夜间休眠期漏发的邮件通知,自动在次日早上补发。
注意:务必确保配置中设置了ADMIN_URL环境变量,并需要添加以下两个云函数定时任务。

目前实现了两种云函数定时任务:(1) 自动唤醒,定时访问 Web APP 二级域名防止云引擎休眠;(2) 每天定时检查 24 小时内漏发的邮件通知。

进入「云引擎」→「定时任务」中,创建定时器,创建两个定时任务。

  • 选择self-wake云函数,Cron表达式为0 0/20 7-23 * * ?,表示每天早上 7 点到晚上 23:59 点每隔 20 分钟访问云引擎。编辑后会有提示Every 20 minutes, between 07:00 AM and 11:59 PM,如图所示:
    定时任务之唤醒服务器
  • 选择resend-mails云函数,Cron表达式为0 59 7 * * *,表示每天早上 8 点检查过去 24 小时内漏发的通知邮件并补发。编辑后会有提示At 07:59 AM,如图所示:
    定时任务之邮件重发检查

设置完成后,就是如下两个函数,如图所示:

✉ 注意:我们在创建定时任务时的时间和提示时间(即下图「运行周期」)都是基于「零时区」,而下图的「下次执行时间」才是基于本地时区「东八区」。可以看到,邮件重发检查设置的是「早上8点」但执行时间是「下午16点」。
定时任务之两种云函数

3.2.国际版时区问题

LeanCloud官方文档之CRON表达式最后备注的说法:Cron 表达式的时区为东八区(国内版)、UTC 零时区(国际版)
因此,我们使用的「国际版」设置的定时任务和国内时区不匹配,应减去8小时(时区转换参考百度百科-东八区)。

3.3.服务器设置自动唤醒

云函数定时任务时区其实是小问题,因为「唤醒服务器」基本上用不了,而「邮件重发」任何时间都可以。所以上述定时任务的Cron表达式也可以不需要修改,且需停止「唤醒服务器」,如图所示是我停止了自动唤醒且修改了邮件重发:
定时任务之修改时区和停用唤醒


查看日志发现唤醒失败:因流控原因,通过定时任务唤醒体验版实例失败,建议升级至标准版云引擎实例避免休眠,参考《Valine-admin配置攻略之防止服务器休眠》、《优雅解决LeanCloud流控问题之其他解决方案(方案二)》及《Hexo 优化—-支持邮件通知的评论Valine增强版之LeanCloud休眠策略》等文章,在我自己的服务器上利用crontab创建了定时任务:

1
2
3
4
5
6
7
8
9
10
11
[root@aliyun ~]# crontab -e
0 0/20 7-23 * * ? curl https://hwame.avosapps.us
~
~
"/tmp/crontab.OOI2Ka" 1L, 49C written
crontab: installing new crontab
"/tmp/crontab.OOI2Ka":1: bad hour
errors in crontab file, can't install.
Do you want to retry the same edit? y
*/20 7-23 * * * curl https://hwame.avosapps.us
~

注意:crontab不是LeanCloud后台的定时任务,命令有所区别,如果按云函数的配置0 0/20 7-23 * * ?则会报错:errors in crontab file, can't install;因此按赵俊的配置*/20 7-23 * * * curl https://hwame.avosapps.us,其中URL为评论系统后台管理地址(环境变量中的ADMIN_URL,例如https://hwame.avosapps.us),如图所示:
定时任务之服务器crontab

3.4.生产环境运行效果

生产环境下,「防止服务器休眠」及「定时检查漏发邮件」的运行效果如图所示:

  • web1是在阿里云服务器设置的crontab curl,每天早上 7 点到晚上 23:59 点每隔 20 分钟访问云引擎;
  • system是LeanCloud的定时任务云函数resend-mails,每天早上 8 点检查昨日漏发的通知邮件并补发。

防止休眠运行效果


4.评论系统设置必填项

4.1.问题描述

上文花费大量篇幅设置了「邮件提醒」功能,但是如果人家没有添加自己的邮箱呢?那岂不是我辛苦回复了,也无法联系到对方啊!所以需要给评论系统的邮箱字段增加必填的功能。
既然添加必填项要涉及到修改源码,干脆一次性改好,包括上文1.前言提到的拉取QQ头像功能。
所以问题总共有如下几个:

  • 设置必填项requiredFields
  • 自动拉取QQ头像enableQQ
  • 昵称长度小于3的问题。

4.2.源码分析

文件位于./themes/pure/layout/_script/_comment/valine.ejs,源码文件内容见github
可见,在verifynotify已被弃用的情况下,除了必需的appIdappKey以外,pure主题实际只设置了metaplaceholderavatarpageSizevisitor这5个自定义字段。
然而,Valine实际是提供了这些的,详见valine配置项,我们可以仿照valine.ejs中的代码并结合Valine支持的配置项进行添加:

  • 字符串/true/false类型:直接以theme.comment.valine.FIELD的方式获取;
  • 列表类型:仿照meta以「,」分割:
    1
    2
    3
    4
    5
    6
    7
    var FIELD = '<%= theme.comment.valine.FIELD %>';
    FIELD = FIELD.split(',');

    new Valine({
    ...
    FIELD: FIELD,
    });

4.3.修改源码及主题配置文件

Valine提供了众多配置项,但并不都是我们所需要的,除去上文4.2.源码分析提到的已配置的项,我们还需要配置(按./themes/pure/_config.yml文件的配置顺序):

  • enableQQenableQQ: '<%= theme.comment.valine.enableQQ %>',
  • recordIPrecordIP: '<%= theme.comment.valine.recordIP %>',
  • requiredFields
    1
    2
    3
    4
    5
    var requiredFields = '<%= theme.comment.valine.requiredFields %>';
    requiredFields = requiredFields.split(',');
    new Valine({
    requiredFields: requiredFields,
    });

上述三项已在前文Hexo博客搭建(2)——主题配置之评论系统中配置。
另外可能会需要的就是emoji表情了,这里我们不需要这些花里胡哨的,可参考valine自定义表情自行配置(注意:emojiCDNemojiMaps需要同时设置且需对应)。

4.4.昵称长度的问题

最开始是希望设置必填项为「昵称」和「邮箱」两项,且直接写在ejs文件里,测试时发现昵称长度必须大于3,查看引入的js源文件,代码如下(可CtrlF查找定位):

1
2
if(e.config.requiredFields.indexOf("nick") > -1    && E.nick.length < 3)
return v.nick[0].focus(), void e.$el.find(".status-bar").text("" + e.i18n.t("nickFail")).empty(3e3);

逻辑很简单,直接将判断条件中的昵称长度改为E.nick.length < 1即可,亦即昵称非空。但是就还需要将该js文件放到本地,然后引入。
所以我这里妥协了一下,只让必填项为「邮箱」,昵称默认是「Anonymous」。
主题配置文件的配置参考前文Hexo博客搭建(2)——主题配置之评论系统

4.5.修改后内容

修改后完整内容如下:

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
<% if (typeof(script) !== 'undefined' && script) { %>
<script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
<script src="//cdn.jsdelivr.net/npm/valine"></script>
<script type="text/javascript">
var GUEST = ['nick', 'mail', 'link'];
var meta = '<%= theme.comment.valine.meta %>';
meta = meta.split(',').filter(function(item) {
return GUEST.indexOf(item) > -1;
});
var requiredFields = '<%= theme.comment.valine.requiredFields %>';
requiredFields = requiredFields.split(',');

new Valine({
el: '#vcomments',
appId: '<%= theme.comment.valine.appid %>',
appKey: '<%= theme.comment.valine.appkey %>',
verify: <%= theme.comment.valine.verify %>,
notify: <%= theme.comment.valine.notify %>,
placeholder: '<%= theme.comment.valine.placeholder %>',
avatar: '<%= theme.comment.valine.avatar %>',
meta: meta,
pageSize: '<%= theme.comment.valine.pageSize %>' || 10,
enableQQ: '<%= theme.comment.valine.enableQQ %>',
recordIP: '<%= theme.comment.valine.recordIP %>',
requiredFields: requiredFields,
visitor: <%= theme.comment.valine.visitor %>
});
</script>
<% } %>


5.添加身份标签

5.1.前言

此部分主要参考以下文章:

说明:由于网上关于「添加博主标签」都是基于butterfly主题的,而我使用的是pure主题,因此配置方面有较大差别,并且无法显示操作系统浏览器图标。

5.2.修改和配置

样式文件为./themes/pure/layout/_script/_comment/valine.ejs主题配置文件./themes/pure/_config.yml

pure主题关于valine只有上面一个文件,js文件是以<script src="//xxx.js"></script>的形式引入的,原本已引入两个js文件,为了能显示身份标签还需引入https://cdn.jsdelivr.net/gh/HCLonely/Valine@latest/dist/Valine.min.js

1
2
3
<script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
<script src="//cdn.jsdelivr.net/npm/valine"></script>
<script src="//cdn.jsdelivr.net/gh/HCLonely/Valine@latest/dist/Valine.min.js"></script>

由于Butterfly主题使用.pug格式的样式文件,而Pure主题使用.ejs格式的样式文件,两者从主题配置文件获取配置参数的方式不一致:
1
2
3
4
5
6
7
// Butterfly主题valine.pug
window.valine = new Valine({
FIELD: '#{theme.valine.FIELD}',

// Pure主题valine.ejs
new Valine({
FIELD: '<%= theme.comment.valine.FIELD %>',

因此,我们将这部分需要增加的字段不通过.yml文件而是直接写入.ejs文件中。

新引入的js文件与原版相比:

  • 添加博主小伙伴访客标签(可自定义);
  • 添加浏览器和操作系统图标,需fontawesomeV5支持;
  • 邮箱检测更严格;
  • 增强QQ邮箱识别(原版只能通过昵称栏输入QQ号识别);
  • meta placeholder可自定义。

注意:由于此处有三个js文件共存,参数默认值可能与参考文章里的不一致,这里仅针对我修改的Pure主题。
可添加的参数:

参数 类型 说明 默认值
master List/Str 加密的博主邮箱 []
friends List 加密的小伙伴邮箱 []
tagMeta List 身份标签 ["博主","小伙伴","访客"]
metaPlaceholder Dict 自定义占位符 {"nick":"昵称/QQ号(必填)",
"mail":"邮箱(必填)",
"link":"网址(https://)"}
verify Bool 评论是否需要验证 false

参数进一步说明及示例参考:

  • master:md5加密后的博主邮箱,32位长度的字符,邮箱加密可以点击MD5在线加密。示例:['f86269b0b1850cd62ca22b218aea1780']'f86269b0b1850cd62ca22b218aea1780'
  • friends:md5加密后的小伙伴邮箱,32位长度的字符。示例:['1725648a3566b8841f19423519e0a36e', '87b6f0e96e63727d889bb3653fb5867c']
  • tagMeta:显示的身份标签,自定义名称时需要保证数组顺序对应,顺序及颜色依次为「博主」、「小伙伴」和「访客」。示例:['鴻塵', '蜜柚', '访客']
  • metaPlaceholder:原版默认值为{"nick":"昵称","mail":"邮箱","link":"网址(http://)"}并且不支持自定义,新引入的js文件默认值如上表且支持修改。示例:{"nick":"昵称/QQ号","mail":"邮箱(必填)","link":"个人主页"}
  • verify:此选项在官方文档Valine配置项之verify中已废弃,并且用处不大,因此不予配置。

配置完成后的valine.ejs文件只需要在上文4.5节评论系统设置必填项之修改后内容的基础上添加js应用及参数:

1
2
3
4
5
6
7
8
<script src="//cdn.jsdelivr.net/gh/HCLonely/Valine@latest/dist/Valine.min.js"></script>

new Valine({
master: 'f86269b0b1850cd62ca22b218aea1780',
friends: ['1725648a3566b8841f19423519e0a36e', '87b6f0e96e63727d889bb3653fb5867c'],
tagMeta: ['鴻塵', '蜜柚', '访客'],
metaPlaceholder: {"nick":"昵称/QQ号","mail":"邮箱(必填)","link":"个人主页"}
});

5.3.关于昵称长度的说明

正如上文提到的:

原版获取QQ头像是需要在昵称栏输入QQ号,然后生成QQ邮箱,以此匹配头像并将输入的QQ号转换为QQ昵称。如果再修改了昵称,是不会匹配到正确的QQ头像的。
新版增强了QQ邮箱识别,QQ头像只根据邮箱匹配,即使修改了昵称也能正确匹配头像。

Butterfly主题设置的是「昵称」必填,这会导致其长度必须大于等于3,虽然现在修改昵称长度也不会丢失头像,但我们是有三种js共存的!而且上文也取消了「昵称」必填的限制,所以这个问题不需要考虑。

评论测试如下,支持匿名和任意长度,符合我们的预期(因为此邮箱不是QQ邮箱,所以是系统头像):
测试匿名评论及昵称长度

5.4.fontawesomeV5支持

Butterfly主题天然支持fontawesomeV5Pure主题不支持,因此无法显示「浏览器」和「操作系统」的图标,如图所示:
Butterfly和Pure主题图标显示对比

图标是否显示并没有太大区别,因此是否对Pure主题添加fontawesomeV5支持都无所谓。如果有需要,可以参考以下内容进行配置:

解决办法:在样式文件./themes/pure/layout/_script/_comment/valine.ejs中引入外部CSS样式即可,all.cssall.min.css皆可:

1
2
3
4
<link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.1/css/all.min.css" />

PS: 最开始把CSS当做js引入了,结果查错半天,像这种:
<script src="//cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.1/css/all.min.css"></script>

但是仍然无法识别Mac OSQQ浏览器夸克浏览器微信等,这个问题是font-awesome自身的问题。

修改完后的效果见评论区。


6.美化评论区样式

此部分内容参考moyu的文章为博客添加一些小功能以及美化之美化基于valine的评论系统

修改./themes/pure/source/css/style.css,可以添加如下三个部分:

  • 添加评论框背景,编辑评论时自动隐藏;
  • 评论及对应的回复采用了卡片式设计,鼠标经过时的阴影;
  • 鼠标悬停于头像时,添加头像旋转。

将需要的部分直接在文件末尾加上:

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
39
40
41
/* 评论框背景及隐藏 */
#veditor {
background-image: url(https://cdn.jsdelivr.net/gh/drew233/cdn/20200409110727.webp);
background-size: contain;
background-repeat: no-repeat;
background-position: right;
background-color: rgba(255, 255, 255, 0);
resize: vertical;
}
#veditor:focus{
background-position-y: 200px;
transition: all 0.2s ease-in-out 0s;
}

/* 评论及回复添加边框 */
#vcomments .vcards .vcard {
padding: 15px 20px 0 20px;
border-radius: 10px;
margin-bottom: 15px;
box-shadow: 0 0 4px 1px rgba(0, 0, 0, .12);
transition: all .3s
}
#vcomments .vcards .vcard:hover {
box-shadow: 0 0 8px 3px rgba(0, 0, 0, .12)
}
#vcomments .vcards .vcard .vh .vcard {
border: none;
box-shadow: none;
}

/* 评论头像旋转 */
img.vimg {
transition: all 1s
}
img.vimg:hover {
transform: rotate(360deg);
-webkit-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-o-transform: rotate(360deg);
-ms-transform: rotate(360deg);
}

本站采用了后两项,效果见评论区。