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

点赞功能

社交平台的必备良药,谁谁谁什么时候给你点了个赞,点赞什么的貌似已经深入人心了。之前在 @火喵酱 的博客页面有看到说想用 leancloud 实现 do you like me 的点赞功能,之前没接触 leancloud 的 SDK 不知道咋搞,后来就不了了之,然后因为最近做了 valine 的最新评论排行,所以借这个风顺便来做个点赞功能~

点赞按钮

实践操作

和 valine 最新评论一样,首先需要引入 sdk 并初始化完成化后接入具体实现的代码即可(这个就不说了,上篇笔记里有写的)

实现思路

因为我要做的是页面点赞功能,所以可能会比 do you like me 那个稍稍多个步骤。这里再聊下思路,一般做点赞功能都是在本地点击计数之后再把数据上传到云端服务器,访问页面的时候直接根据页面拉取对应的数据填充即可。不过这里面有个读取和储存数据的操作,在 leancloud 文档里明确说到:

由于赞和转发的操作可能由多个客户端同时进行,直接在本地更新数字并保存到云端的做法极有可能导致差错。

所以 leancloud 官方提供了一个 increment 函数来实现“原子操作”跳过读取储存操作直接计数统计(已经试过了传统方法也是可行的,不过使用该函数会更简便)

具体实现

首先我们在 leancloud 控制台创建一个自定义名称的 class 数据(比如:likeCount) ,然后构建对象

需要手动创建 class 类!

   var URL = location.pathname,  //获取纯 url 防止重复筛选
        el = document.getElementById("counter"),  //写入元素
        el_ = document.getElementById("like");  //点击元素

    const likeCount = AV.Object.extend("likeCount"),  //为 AV.Object 创建子类
          likeDemo = new likeCount(),  //为该类创建一个新实例
          getCounter = new AV.Query("likeCount"),  //使用 AV.Object 的构造器
          getCount = getCounter.equalTo("url",URL);  //用于筛选指定 url 的 like 计数

完成后进行初始化获取计数判断是否有数据存在

getCount.find().then(results => {
    getCount.find().then(results => {
        //成功返回数据=>获取云端计数=>写入到元素 || 返回空数据=>写入到元素
        results.length>=1 ? el.innerHTML = results[0].attributes.num : el.innerHTML = 0;
    });
});

初始化获取后创建模拟点击函数

   el_.onclick=function(){  //元素点击函数
        getCount.find().then(results => {
            if(results.length>=1){  //成功返回数据(已创建对象,更新 num 数据)
                let countNUM = results[0].attributes.num,  //获取云端计数
                    objId = results[0].id;  //获取当前页面 id
                countNUM++;  //本地计数++
                el.innerHTML = countNUM;  //写入本地计数到元素
                const increase = AV.Object.createWithoutData('likeCount', objId);  //获取当前页面 id 的云端对象
                increase.increment('num', 1);  //使用 increment 函数更新云端对象的 num 属性
                //储存(更新)对象到云端
                increase.save().then(increase => {
                    console.log("Update Successfully.");
                }, error => {
                    console.log("Update Error.")
                })
            }else{  //返回空数据(未创建对象,第一次创建num+url)
                let countNUM = 0,  //初始化本地计数
                    countURL = URL;  //获取当前 url
                countNUM++;  //本地计数++
                el.innerHTML = countNUM;  //写入本地计数到元素
                //储存 num(本地计数) 和 url(当前url) 对象到云端
                likeDemo.save({
                    'num': countNUM,
                    'url': countURL
                }).then(result => {
                    console.log("Save Successfully.");
                }, error => {
                    console.log("Save Error.")
                })
            }
        })
    }

最后在执行代码前不要忘了判断一下 el_ 元素是否存在,如果不存在表示当前页面不需要获取 likeCount 数量则不发送请求

   if(el_!=null && el_!=undefined){
        //..
    }

其他

以上代码写入 SDK 初始化后运行就可以正常获取不同页面的点赞次数了,数据创建后会在 leancloud 控制台显示,如下

其实拿到信息还可以通过点赞数量创建热门文章(还可以加个 pageview 实现页面访问量)具体实现方式和最新评论差不多,喜欢就做(页尾会拓展一个集成统计页面访问)

一个 num 一个 url

问题修复

期间遇到不少问题,在文档里又没说明白,让人属实脑壳疼,400/400各种报错,在这也做个简单的记录以免再踩坑。

获取不到 class 404

出现这个问题的根本在构造对象完成后,没有进行数据储存操作,导致 SDK 没有自动创建我们指定的 class。解决方案很简单,在控制台手动创建一个同名称的 class 或在完成构造对象后发起一个储存请求,该请求会自动创建 class

推荐第一种方案,因为我现在也还没解决这个问题/笑哭

获取不到 class

错误的数据写入类型 400

这个问题困扰了我好久,因为正常写入 String 类型都是可以的,当我把 num 以 Number 类型写入就会报错写入失败,找半天没找到原因最后发现原来是创建 class 之后第一次储存的类型会一直保留(比如查询到空数据,然后第一次写入 num 为 String 类型,第二次写入 Number 类型就不行了..)

解决方案也很简单,第二次写入类型要和第一次写入类型相同(当时找半天没找到原因我是直接把 num 转成 string 类型储存了233)

第二次写入 undefined 了

相关链接

LeanCloud 官方文档

拓展

分享功能已经完善的差不多了(目前点赞和海报生成暂未开放仅限测试页面目前已全站开放,欢迎测试报bug~)下次聊下如何设置博主显示和置顶评论以及利用 html2canvas+qrcodejs 生成文章分享海报~

上面有写到利用该方法统计页面访问量的情况,简单列一下

var URI = location,
    URL = URI.pathname,
    el = document.getElementById("counter"),
    el_ = document.getElementById("like"),
    els = document.getElementById("viewer"),
    COUNTER = AV.Object.extend("COUNTER"),
    countDemo = new COUNTER(),
    getCounter = new AV.Query("COUNTER"),
    urlCheck = getCounter.equalTo("url",URL);
//定义数据储存函数
const saveAttr=(like,view)=>{
    let countURL = URL;
    countDemo.save({
        'like': like,
        'view': view,
        'url': countURL
    }).then(result => {
        console.log("Save Successfully.")
    }, error => {
        console.log("Save Error.")
    })
},
//定义数据更新函数
updateAttr=(id,attr,num)=>{
    let increase = AV.Object.createWithoutData('COUNTER', id);
    increase.increment(attr, num);
    increase.save().then(increase => {
        console.log("Update Successfully.")
    }, error => {
        console.log("Update Error.")
    })
};
// 首次(刷新)访问页面执行 query.view+like autoCheck
urlCheck.find().then(results => {
    if(results.length>=1){
        //返回有数据时
        let upAttr = 'view',  //需要获取的数据属性名
            objId = results[0].id,  //云端id
            viewNum = results[0].attributes.view,  //获取云端 view 数据
            likeNum = results[0].attributes.like;  //获取云端 like 数据
        viewNum++;  //更新 view 数据
        el!=null&&likeNum!=undefined ? el.innerHTML=likeNum : el.innerHTML=0;   //满足条件写入元素
        updateAttr(objId,upAttr,1);  //调用数据更新函数发送到云端
        els!=null ? els.innerHTML = viewNum : false;  //update之后执行(即使没有找到可用计数器也能记录并发送数据)
    }else{
        //返回空数据时
        var likeNum = viewNum = 0;  //第一次访问(刷新)页面初始化 view/like
        el.innerHTML = likeNum;  //写入空数据到元素
        viewNum++;  //更新 view 数据
        els!=null ? els.innerHTML = viewNum : false;  //满足条件写入元素
        saveAttr(likeNum,viewNum)  //调用数据储存函数发送到云端
    }
});
//点击按钮执行 query.like Click
el_.onclick=function(){
    urlCheck.find().then(results => {
        if(results.length>=1){
            //返回有数据时
            let upAttr = 'like',  //需要获取的数据属性名
                objId = results[0].id,  //云端id
                likeNum = results[0].attributes.like;  //获取云端 like 数据
            //likeNum==undefined ? likeNum=0 : likeNum;  //判断 like 数据是否未定义再执行(已废除,第一次访问页面会初始化点击次数)
            likeNum++;  //更新 like 数据
            el.innerHTML=likeNum;  //写入数据到元素
            updateAttr(objId,upAttr,1) //调用数据更新函数发送到云端
        }else{
            console.log("data init error!")
            /*/返回空数据时(已废除,首次访问页面即可完成数据初始化)
            var likeNum = 0,
                viewNum;
            likeNum++;
            el.innerHTML = likeNum;
            saveAttr(likeNum,viewNum)  //调用数据储存函数发送到云端*/
        }
    })
}

以上代码正确配置后无报错应该如下图所示。

view数据实时更新 / like数据点击更新

以上,有问题评论区留言。