解决文章阅读统计与valine的冲突

重写主题后发现文章阅读次数统计和valine又冲突了。LeanCloud官方给的解决方法就是等SDk版本到5.0或者使用REST API。

其他版本的valine内置了文章阅读次数统计。但valineEx没有,只能自己来了。

以下函数中的app_idapp_key是我之前定义好的常量的,就是LeanCloud的appid和key。测试的时候使用xhr一直报跨域错误但是fetch不会,所以全部用的fetch,反正兼容性还凑合。

与网上其他方法相比的优点

  • 不需要引入80几kb的sdk
  • 不会和其他依赖sdk的应用冲突valine
  • 合并请求,避免响应429

网上的文章都是引入0.6.4的sdk,然后给3个函数。。这么多年了都没有个新文章。

LeanCloud REST API 文档

LeanCloud国内版可以使用https://api.leancloud.cn这个域名,也可以使用自己绑定的或者共享域名。国际版不用备案域名可以绑自己域名。

最后就是LeanCloud的安全问题。。。暂时没办法。

function initCount() {
  if (document.getElementsByClassName('leancloud_visitors').length === 1) {
    addCount();
  } else if (document.getElementsByClassName('leancloud_visitors').length > 1) {
    showTime();
  }
}

为避免页面中只有一篇文章的情况,第一个if应该加个条件(只有文章内页才有的dom),已经水了这么多文章懒得加了。

页面初始化或者路由变化时调用initCount就可以了。

function showTime() {
  queryCount()
  .then(response => response.json())
  .then(result=> {
    result.results.forEach(i => {
      document.getElementById(i.url).innerText = i.time;
    });
  })
  .catch((error)=>{})
}

用于首页显示文章阅读次数,解决了网上旧方法每篇文章一个请求的蠢方法。

function addCount(){
  const visItem = document.getElementsByClassName('leancloud_visitors')[0];
  const url = visItem.getAttribute('id').trim();
  const title = visItem.getAttribute('data-flag-title').trim();
  queryCount(url)
  .then(response => response.json())
  .then(result => {
    if (result.results.length === 0) {
      createCount(title,url)
      .then(response => response.json())
      .then(result => {
        document.getElementById(url).innerText = result.time;
      })
    }else{
      const id = result.results[0].objectId;
      updateCount(id)
      .then(response => response.json())
      .then(result => {
        document.getElementById(url).innerText = result.time;
      })
    }
  })
  .catch((error)=>{})
}

进入文章内页会调用此函数,数据库中查询不到就调用发送创建请求的函数。

function queryCount(url){
  const QueryObj = {
    _method:"GET",
    _ApplicationId: app_id,
    _ApplicationKey: app_key
  };
  if (url) {
    QueryObj.keys = 'time';
    QueryObj.limit = 1;
    QueryObj.where = {url};
  } else {
    QueryObj.keys = "time,url";
    const URl_Arr = [];
    const visItem = document.querySelectorAll('.leancloud_visitors');
    visItem.forEach(function (el) {
      URl_Arr.push(el.getAttribute('id').trim());
    });
    QueryObj.where = {url:{$in:URl_Arr}};
  }
  const raw = JSON.stringify(QueryObj);
  const requestOptions = {
    method: 'POST',
    headers: {"Content-Type": "text/plain"},
    body: raw
  };
  return fetch('https://api.leancloud.cn/1.1/classes/Counter', requestOptions)
}

不管是首页显示文章阅读次数还是文章内页,更新计算器都需要执行这个函数获取阅读次数和id。按照LeanCloud文档中写的发get请求结果就报错了。只能用特殊手段,旧版的sdk也是用这方法。

function updateCount(id) {
  const raw = JSON.stringify({time:{__op:"Increment",amount:1}})
  const reqOptions = {
    method: 'PUT',
    headers: {
      "Content-Type": "application/json",
      "X-LC-Id": app_id,
      "X-LC-Key": app_key
    },
    body: raw,
  }
  return fetch('https://api.leancloud.cn/1.1/classes/Counter/'+id+'?fetchWhenSave=true', reqOptions)
}

更新计数器的函数,id通过queryCount得到。

function createCount(title,url){
  const obj = {
    title,
    url,
    time:1
  };
  const raw = JSON.stringify(obj)
  const reqOptions = {
    method: 'POST',
    headers: {
      "Content-Type": "application/json",
      "X-LC-Id": app_id,
      "X-LC-Key": app_key
    },
    body: raw,
  };
  return fetch('https://api.leancloud.cn/1.1/classes/Counter?fetchWhenSave=true', reqOptions)
}

新文章初始化数据的函数。

写给小白

在主题模板里添加下面的html代码,然后把之前的js加到博客的js中。代码执行后会自动查找页面中class值为leancloud_visitors的元素,获取其id为查询条件。并将得到的值填充进去。

id是文章的相对路径,data-flag-title是文章标题。存储到LeanCloud时需要用这些内容。

<span class="iconfont iconeye" >
  <span id="<%- url_for(post.path) %>" class="leancloud_visitors" data-flag-title="<%- post.title %>"></span>
</span>

解决文章阅读统计与valine的冲突

本文作者
Track13
发布于
2020-12-23
许可协议

转载或引用本文时请遵守许可协议,注明出处、不得用于商业用途!

《解决文章阅读统计与valine的冲突》上有条评论