部落格現在用的評論是 Valine
, 是一款基於 Leancloud 的快速、簡潔且高效的無後端評論系統。看配置什麼都挺簡單的,也有不少教程,本來沒想寫這個,但實際在訪問量統計這個功能上遇到不少問題(仍然是需要一定開發工作的),找到的教程都挺亂,乾脆自己重新梳理下了。
我的部落格 blog.liluhui.cn, 歡迎右上角 RSS 訂閱 ~
Before. 配置好 Valine
先參照 Valine官網教程 把應用配置好,不要遺漏安全域名,至此你的評論功能應該已經好了。
如果你不需要評論功能,或者不需要 Valine 來實現評論功能,可以跳過引用 Valine,但仍然需要建立好 Leancloud 的應用。
Start.訪問量統計
|前因
雖然 Valine 文件中說明從 v1.2.0 版本開始支援了文章閱讀量統計
new Valine({ el:'#vcomments', ... visitor: true // 閱讀量統計 }) 如果開啟了閱讀量統計,Valine 會自動檢測 leancloud 應用中是否存在Counter類,如果不存在會自動建立,無需手動建立~ Valine會自動查詢頁面中class值為leancloud-visitors的元素,獲取其id為查詢條件。並將得到的值填充到其class的值為leancloud-visitors-count的子元素裡:
以上為官方說明,但配置並沒有軟用,因為查下原始碼可以看見,Valine 除了在 leancloud 中建立一個 Counter
類什麼都沒有幹 !...
這裡從一些其他資料看是具體邏輯是在 Hexo-theme-next
這個主題做了處理,也就是具體業務方做了儲存和獲取資料的邏輯,挺不合理的,還是應該 Valine 封裝好才對。
至此,我們只能自己動手做儲存和訪問的邏輯了。
|動手
思路:
- 約定 HTML 節點用於採集和渲染統計量資料
- 帶有
class
為leancloud_visitors_count
的表示需要渲染資料,節點屬性data-index
表示唯一索引。我這個場景是 url 為唯一索引,舉例如
<span class="leancloud_visitors_count" data-index="/2019/03/12/ZRender使用記錄/"></span>
複製程式碼
- 帶有
id
為leancloud_visitors
的表示採集資料,節點屬性data-index
表示唯一索引。舉例如
<span class="leancloud_visitors_count" data-index="/2019/03/12/計算字的寬高/"></span>
複製程式碼
-
JS實現方法
addCount
用於上傳資料(指定索引訪問量+1)和showTime
用於渲染訪問量資料 -
要渲染的所有索引批量查詢一次
-
減少請求,每個頁面採集只允許一次,所以採集資料約定的節點為 id 而不是 class
S1. Leancloud 建 Counter 類
S2. JS 程式碼實現
同 Hexo 的使用者,把這段程式碼單獨存檔案 _partials/lean-analytics.swig
,在對應主題目錄下 ./layout/javascript.swing
中引用這份檔案即可
以下程式碼根據你的實際場景做些處理,你要儲存哪些欄位、你的 Leancloud 的 appId 和 appKey、你的節點約定規則等。
// 這段程式碼依賴 jquery,請檢查下是否有引用
// 具體的HTML節點約定你可以根據你的需要更改
<script src="https://cdn1.lncld.net/static/js/av-core-mini-0.6.1.js"></script>
<script>
function showTime(Counter) {
var query = new AV.Query(Counter);
var $doms = $(".leancloud_visitors_count")
if (!$doms.length) return
// var urls = $doms.map(function(){return $(this).attr('data-index')}) || [];
var urls = []
for (var i = 0; i < $doms.length; i++) {
urls.push($($doms[i]).attr('data-index'))
}
query.containedIn("url", urls);
query.find({
success: function(rst) {
// 初始化
$doms.each(function(i) {
$(this).text(0)
})
// 渲染資料
for (var i in rst) {
var item = rst[i]
$('.leancloud_visitors_count[data-index="'+ item.get('url') +'"]').text(item.get('time'))
}
},
error: function (obj, e) {
console.warn(obj, e)
}
})
}
function addCount(Counter) {
var Counter = AV.Object.extend("Counter");
var $dom = $("#leancloud_visitors")
if (!$dom.length) return
url = $dom.attr('data-index').trim();
var query = new AV.Query(Counter);
query.equalTo("url", url); // 我的需求以儲存的url為唯一索引
// Case 有這條資料則time欄位+1
query.find({
success: function(results) {
if (results.length > 0) {
var counter = results[0];
counter.fetchWhenSave(true);
counter.increment("time");
counter.save(null, {
success: function(counter) {
},
error: function(counter, e) {
console.warn(counter, e)
}
});
}
// Case 沒有這條資料則新增一行資料
else {
var newcounter = new Counter();
var acl = new AV.ACL();
acl.setPublicReadAccess(true);
// acl.setWriteAccess(AV.User.current(),true);
acl.setWriteAccess('*', true);
newcounter.setACL(acl)
// 這裡是你想儲存的資料
newcounter.set("title", title);
newcounter.set("url", url); // 我的需求以儲存的url為唯一索引
newcounter.set("time", 1);
newcounter.save(null, {
success: function(newcounter) {
},
error: function(newcounter, error) {
console.log('Failed to create');
}
});
}
},
error: function(e) {
console.warn(e)
}
});
}
$(function() {
// @TODO
// appId 和 appKey 在 Leancloud 應用的設定內可以找到,
// 具體可參考上面 Valine 的官方引導吧。
AV.initialize(appId, appKey);
var Counter = AV.Object.extend("Counter");
addCount(Counter);
showTime(Counter);
});
</script>
複製程式碼
S3. HTML 節點新增
這是我在文章列表的訪問量渲染節點
<span class="reading-time">
<span class="leancloud_visitors_count" data-index="{{ url_for(post.path) }}"></span>
read
</span>
複製程式碼
這是我在每個文章頁面的採集節點
<div id="leancloud_visitors" data-index="{{ url_for(page.path) }}" data-title="{{ page.title }}"></div>
複製程式碼
問題集
ACL 許可權問題
出現報錯 ACL 許可權問題,是因為建立 Counter 類的時候選擇了限制寫入作為ACL的預設設定。
參考 ACL 許可權管理開發指南],可以通過建立資料行時宣告 ACL 許可權解決
// 新建一個帖子物件
var Post = AV.Object.extend('Post');
var post = new Post();
post.set('title', '大家好,我是新人');
// 新建一個 ACL 例項
var acl = new AV.ACL();
acl.setPublicReadAccess(true);
acl.setWriteAccess(AV.User.current(),true);
// 將 ACL 例項賦予 Post 物件
post.setACL(acl);
post.save().then(function() {
// 儲存成功
}).catch(function(error) {
console.log(error);
});
複製程式碼
臨時處理上,可以在後臺直接編輯修改