valine leancloud 09/11/2020
最近更新于:

最新评论

这是个比较好用的功能,不管是用户还是管理员都可以即时看到最新评论并作出回应。只可惜 valine 官方并没有开发这么一个功能,秉承着自己动手丰衣足食的想法,也算是在 @Lee 的一篇 leanCloud 文章的启发下才有了使用 leanCloud 实现最新评论的思路。

展现形式

虽说是最新评论,不过我看了下也有几种不同的展示形式,其中一种是直接获取【用户+评论数据】另一种则是【用户+评论地址】第一种的话比较好实现,不用额外代码就可以做到

实现评论页面的获取形式各不相同

两种评论展示差异

因为实现及体验的缘故,暂时展示的是用户+评论内容,这里主要做下使用 leanCloud 获取最新评论的思路

LeanCloud

使用 valine 的同学应该很清楚 valine 虽说是一款无后端评论系统,可它也是基于 leanCloud 提供的云储存SDK服务才能使用,不过很多同学应该是直接在 valine.js.org 官网就直接上手了,leanCloud 官网的文档比较杂就理所当然没什么人去看,再一个 valine 官网提供的服务已经完美满足大部分人的评论需求了,有特殊需求还可以去项目地址提 issues。

不过要知道,issue 提出可能会被否定,这可能考虑到众多因素的关系,必须大多数人同意且作者有时间去做才会在下次版本中更新出来,而像最新评论这种情况,本身就是调用 leancloud SDK来实现的东西,基本上官方是不会出的,毕竟请求过多又是免费版。

后台

在 leancloud 后台评论管理系统中有一个域名绑定的东西,绑定了 云引擎、ClientEngine 域名 就可以发现通过绑定的域名可以登录访问评论后台,而这个后台也就是通过 SDK 实现的,这样一来就清晰明了了。

API域名绑定

通过绑定 api域名 后我们可以访问 leanCloud 提供的 SDK,进而获取对应的最新评论(绑定操作不细述,按他提示操作就行,注意国内版需要备案才可以绑定)

域名绑定完成后,提供官方提供的文档说明进行操作


实现思路

首先需要引入 leancloud 提供的 SDK(使用官方提供的CDN即可)

引入SDK

                        <script src="//cdn.jsdelivr.net/npm/leancloud-storage@4.6.1/dist/av-min.js"></script>
                        

初始化 SDK

同样的,使用官方提供的初始化SDK函数进行SDK初始化,这里需要注意的是不能直接初始化SDK,因为我们使用的 valine.min.js 已经初始化过一次了,实践重复初始化SDK会导致意外报错!同时我们也不需要重新定义 serverURLs,valine 会自动处理我们绑定的服务器地址

不要重复初始化SDK!

这里和官方提供的文档有所不同的是需要在 valine.min.js 中找到相应的初始化代码,然后插入后续代码即可。


我们在 valine 中搜索 AV.init 即可找到 valine 初始化 SDK 的位置,以下

M !== AV._config.applicationId && C !== AV._config.applicationKey && AV.init({
        appId: M,
        appKey: C,
        serverURLs: T
    });

然后在上面这段代码之后,执行查询(写入数据这里不列,因为获取评论数据只需要用到查询)

const query = new AV.Query("Comment");  //新建 Comment 列的查询
    query.find().then(results=>{
        console.log(results);  //返回该列所有查询结果
    })

查询条件

上面代码执行后会查询所有评论,但是是倒序查询,这完全不符合我们最新评论的要求了,所以我们需要添加 ascending(次序)descending(顺序) 方法来决定输出评论的顺序(createdAt 是排序条件)

query.descending('createdAt').find().then(results=>{
        //..
    })

使用 descending('createdAt') 后会顺序输出 Comment 列的最新评论,达到了我们想要的最新评论效果。ok 那么问题又来了,我不想输出所有的评论,我只想输出前 5 个最新评论怎么搞?

我在官方文档没找到,后来在一个项目里找到了,使用 limit() 方法就可以了

query.descending('createdAt').limit(5).find().then(results=>{
        //..
    })

写入评论

按需查询评论后返回对象写入即可,这里直接贴出完整代码,以下是出现过的问题,注释里会写出来

  • 重复初始化 valine 的时候,会造成重复请求最新评论
  • 拿到的评论内有标签元素不能直接插入,需要正则匹配去掉后再写入
  • 获取指定 id 为 undefined 时,使用 vcomments 代替 id 用做 hash 跳转
const query = new AV.Query("Comment"),  //定义查询列
          box = document.getElementById("comments"),  //获取写入元素
          max = 5,  //设置最大写入数量
          boxmax = box.children.length;  //获取已写入元素数量
    query.descending('createdAt').limit(max).find().then(results=>{
        for (let i = 0; i < results.length; i++) {
            let res = results[i], 
                ids = res.id,
                nick = res.attributes.nick, 
                comment = res.attributes.comment, 
                urls = res.attributes.url;
            //判断已写入数量是否大于最大写入数量,是则不操作(已写入),否则根据以上条件写入评论到元素 ↓
            boxmax <= max ? box.innerHTML += `<li><a href="${urls}#${ids}"target="_blank"><em title="${comment}">${nick}:${comment}</em></a></li>` : console.log('repeating request(init)! newest comments has already been requested.')
        }
    })

以上代码已经可以正常写入评论,不过存在一个问题就是在我个人修改后的 valine 中,评论的表情会以 <img> 标签的形式写入评论,这样很不利于查看具体评论了什么,因为现实评论的字符是有限的,所以在写入元素之前需要对 comment 进行过滤

trouble shooting

这里直接创建一个过滤标签的函数,写入元素之前调用过滤一遍即可

//创建删除标签元素函数后续调用
    function noTags(str){
        var a=str.replace(/<\/?.+?>/g,""),
            b=a.replace(/ /g,"");
        return b;
    }

    //此处省略其他代码..

    var comment_t =  noTags(comment); //调用删除标签函数,将换评论转换为文本形式
    boxmax <= max ? box.innerHTML += `<li><a href="${urls}#${ids}"target="_blank"><em title="${comment_t}">${nick}:${comment_t}</em></a></li>` : console.log('repeating request(init)! newest comments has already been requested.')

附加内容

看到这里基本上对于使用 leanCloud 获取 Valine 的最新评论已经很明确了,不过!正如笔记开头所说的,最新评论有2种形式,以上只是实现了第一种,那么第二种【用户+评论地址】又该如何实现,其实这里我已经实现了,可以通过下面的 iframe 看一下

其实实现原理很简单,使用 jQuery ajax 进行异步请求(这里有个点稍后会说)再利用 load() 方法获取指定页面的指定标签内容,也就是 title 标签,很简单也很粗暴,不过其中有些许问题让我琢磨了不少时间才解决了问题

评论地址的获取

有关 load() 方法网上介绍有很多,只需要提供相应的 url 和具体需要的内容即可,以下是基本语法(顺带一提 ajax 仅能请求到同源数据,这限制于浏览器的同源保护策略,不过这对获取本站页面数据来说倒不是问题)

$("div").load('URL TAGS',function(responseTxt,statusTxt,xhr){
        //url后跟随请求页面的具体内容,测试可填写标签名、id、class等,请求成功后会直接完整填充到指定元素
    });

了解了以上方法后可以直接套用在第一种 用户+评论数据 的请求里,像这样

//执行jQuery ajax 异步请求(urls对应上面的具体页面url)
    $("#fetch").load('//blog.2broear.com'+urls+' title',function(responseTxt,statusTxt,xhr){
        var html=$(this).html(),
            text=noTags(html),
            r1=text.replace("|", ""),
            r2=r1.replace(/2BROEAR/g, "");
        boxmax <= max ? box.innerHTML += `<li><a href="${urls}#${ids}"target="_blank"><em title="${comment}">${nick}:${comment} <small>回复自— <b>《${r2}》<b></small></em></a></li>` : console.log('repeating request(init)! newest comments has already been requested.')
    });

发现已经可以正常请求到页面指定的 title 标签并提供正则匹配我们想要的字符串然后正确的写入到对应评论里了,不过问题又又又来了,这个就是我之前提到的那个点存在的问题,即

每次刷新后评论的顺序混乱了

没错,这个问题直接导致了最新评论写入到元素后排序发生了改变,完全打乱了最新评论,但请求的的数据顺序却没有问题,想了很多方法都没解决,然而最终解决方案却特别简单..

排序混乱解决方案

首先我们知道 ajax 是异步请求的,所以请求的内容全部都不会被浏览器堵塞而是一块加载的,这就直接导致了每次加载的顺序不一样。而影响加载顺序的因素却有很多,你比方执行的快与慢与响应的数据量的大小及后台逻辑的复杂程度都有关系,所以不论前台怎么调整,它通过异步请求是始终不变的,所以我们直接

$.ajaxSetup({
        async: false  //关闭异步请求,同步加载
    });

没错,直接把 ajax 的异步请求关闭之后所有请求都会同步进行而不会造成一起加载的情况了,完成后再怎么刷新都会按照 leancloud 获取的数据顺序进行排序了。


结尾总结

总结一句就是条条大路通罗马,

不同的思路却可以实现相同的功能,这也算是折腾的乐趣吧

最后贴下上面 iframe 的完整代码(个人不建议使用该方法请求页面数据,一个是性能问题还有一个就是 leancloud 可能会限制什么的,反正我只用了第一种用户+评论数据的方案)

注意

在 valine 中需要设置一个定时器再执行查询,避免查询到空数据报错()

完成后可以选择封装或直接将代码复制到上面提到的 AV.init 初始化后面

var delayLoad = setTimeout(function() {
        function noTags(str){
            var a=str.replace(/<\/?.+?>/g,""),
                b=a.replace(/ /g,"");
            return b;
        };
        const query = new AV.Query("Comment"),
            box = document.getElementById("comments"),
            max = 5,
            boxmax = box.children.length;
        query.descending('createdAt').limit(max).find().then(results=>{
            for (let i = 0; i < results.length; i++) {
                let res = results[i], 
                    ids = res.id,
                    nick = res.attributes.nick, 
                    comment = res.attributes.comment, 
                    urls = res.attributes.url;
                $.ajaxSetup({
                    async: false
                });
                $("#fetch").load('//blog.2broear.com'+urls+' title',function(responseTxt,statusTxt,xhr){
                    var html=$(this).html(),
                        text=noTags(html),
                        r1=text.replace("|", ""),
                        r2=r1.replace(/2BROEAR/g, "");
                    boxmax <= max ? box.innerHTML += `<li><a href="${urls}#${ids}"target="_blank"><em title="${comment}">${nick}:${comment} <small>回复自— <b>《${r2}》<b></small></em></a></li>` : console.log('repeating request(init)! newest comments has already been requested.')
                });
            }
        })
        clearTimeout(delayLoad)
    }, 500);

参考链接

巧用LeanCloud实现在线留言板功能 详解Ajax请求(四)——多个异步请求的执行顺序


以上。