Lazyload 不让 Valine 评论组件拖慢页面加载
我一直将速度作为衡量一个网站的重要指标。就算一个网站再精美,但每个动作都要花上 10s 来完成,想必体验也不会好到哪去。而即便 Valine 作为一款简介强大的评论组件,按目前效果来看也仍然有着可观的优化空间。
老朋友 lazyload
Lazyload 在前端性能优化领域已是老生常谈,为了不让一些非关键资源和主要内容争夺带宽和性能,任何媒体资源、DOM 节点、非关键 JS 和 CSS 都可以使用 lazyload 优化。
而 Lazyload 的实现也非常简单,用一个 JavaScript 监控当前页面位置,当元素出现或即将出现在可视区域时才加载资源,而非传统的一上来就把所有资源加载完。
现在是公元 2020 年,还有相当一部分插件绑定 scroll 事件实现 lazyload ,在页面滚动的时候这个函数会被频繁触发,十分浪费性能。相比之下,调用 IntersectionObserver 则是一种更优雅的解决方案,这个 API 会自动「观察」元素是否在可视范围内,早在 Chrome 51 便支持了这一功能,其他现代浏览器也均有支持。除非你真的很在意旧版 IE 用户,不然都建议使用后者。
![](https://cdn.jsdelivr.net/gh/ichralpha/imgbag/20200319190913.png)
(IntersectionObserver 浏览器支持情况)
Valine ? Yes !
市面上主流的静态博客评论插件其实并不算很多,像是我在使用的 Hexo 主题 Volantis 就适配了 Gitalk, Disqus, Valine 和 Livere 。
可是可能是最老牌好用的 Disqus 已经被墙,尽管有人开发了 DisqusJS 但是需要注册登录才能评论的体验还是有点割裂。 韩国的 Livere 来必力也是需要注册登录才能评论,而且国内访问速度一般,绝对称不上优秀。Gitalk / Gitment 必须要使用 GitHub 账号才能评论,而且受到 GitHub Issue Label 的限制,后端管理也不方便。
最后我还是选择了 Valine ,界面简洁,基于免费版的 LeanCloud 就能轻松跑起,配合 Valine Admin 插件也能获得还说得过去的管理后台。
![](https://cdn.jsdelivr.net/gh/ichralpha/imgbag/20200313221348.png)
确实,相较于 Disqus 和 Livere ,Valine 的加载速度已经优秀得太多了。但是 Gravatar 头像在国内即便加了第三方 CDN 速度还是不敢恭维,而且全 markdown 语法支持也让插入大量媒体内容成为可能,甚至这些媒体内容还不遵循文章板块的 lazyload 。尽管这些不能一味把锅甩给 Valine ,但是确实让我某个页面的资源加载大小从 600K 增至 2M ,加载时间从 2s 增至 3.7s 。
而我在谷歌搜索「valine
+ lazyload
」的关键词时,清一色都是关于加入 Valine 评论插件和图片 lazyload 这些揉在一起的教程,完全没有我真正要找的让 ValineJS 本身 lazyload 的插件。
好吧,既然 lazyload 实现也不复杂,那我就自己写一个。
Lazyload 在 Valine 上实践
Valine 是在加载了 Valine.js 后调用 init 方法来进行用户初始化的。如果仅仅是将 Valine.js 换成 lazyload JS 的话势必会导致依赖问题。考虑到博客即便有许多页面,每个页面的 Valine 配置其实都是一致的,所以我的想法是使用一个专门的 JS 如 valine_config.js
单独放置初始化内容:
'use strict'
var valine = new Valine();
valine.init({
el: '#valine_container',
appId: '<APP_ID>',
appKey: '<APP_KEY>',
notify: false,
verify: false,
avatar: '',
placeholder: 'just go go'
});
配置记得换成自己的,细节可以看 这里 。
然后就是 lazyload JS 了,代码很短,我写的只有 30 几行,你可以尝试着自己实现一下。要注意下加载的顺序,默认可不是按照填放的顺序加载,特别是这些相互依赖的部分。
'use strict'
function addScript(url) {
var s = document.createElement('script');
s.setAttribute('src', url);
s.async = false
document.body.appendChild(s);
}
function loadValine() {
addScript('https://unpkg.com/valine/dist/Valine.min.js');
addScript('{ your config path }');
}
var runningOnBrowser = typeof window !== "undefined";
var isBot = runningOnBrowser && !("onscroll" in window) || typeof navigator !== "undefined" && /(gle|ing|ro|msn)bot|crawl|spider|yand|duckgo/i.test(navigator.userAgent);
var supportsIntersectionObserver = runningOnBrowser && "IntersectionObserver" in window;
setTimeout(function () {
if (!isBot && supportsIntersectionObserver) {
var io = new IntersectionObserver(function(entries) {
if (entries[0].isIntersecting) {
loadValine();
io.disconnect();
}
});
io.observe(document.getElementById('{ your Valine container ID }'));
} else {
loadValine();
}
}, 1);
嗯,不是一个回调函数的事情吗,为什么要用两个 script 徒增连接数呢?
'use strict'
function loadValine() {
var s = document.createElement('script');
s.setAttribute('src', 'https://unpkg.com/valine/dist/Valine.min.js');
s.async = false;
document.body.appendChild(s);
s.onload = () => {
var valine = new Valine();
valine.init({
el: '#valine_container',
appId: '<APP_ID>',
appKey: '<APP_KEY>',
notify: false,
verify: false,
avatar: '',
placeholder: 'just go go'
});
}
}
var runningOnBrowser = typeof window !== "undefined";
var isBot = runningOnBrowser && !("onscroll" in window) || typeof navigator !== "undefined" && /(gle|ing|ro|msn)bot|crawl|spider|yand|duckgo/i.test(navigator.userAgent);
var supportsIntersectionObserver = runningOnBrowser && "IntersectionObserver" in window;
setTimeout(function () {
if (!isBot && supportsIntersectionObserver) {
var io = new IntersectionObserver(function(entries) {
if (entries[0].isIntersecting) {
loadValine();
io.disconnect();
}
});
io.observe(document.getElementById('{ your Valine container ID }'));
} else {
loadValine();
}
}, 1);
看来一个外行乱玩代码还是需要点时间来沉淀的呀。
评论