ChrAlpha's Blog

Thumbnail-Lazyload%20%E4%B8%8D%E8%AE%A9%20Valine%20%E8%AF%84%E8%AE%BA%E7%BB%84%E4%BB%B6%E6%8B%96%E6%85%A2%E9%A1%B5%E9%9D%A2%E5%8A%A0%E8%BD%BD

Lazyload 不让 Valine 评论组件拖慢页面加载

2020-03-13·技术向

我一直将速度作为衡量一个网站的重要指标。就算一个网站再精美,但每个动作都要花上 10s 来完成,想必体验也不会好到哪去。而即便 Valine 作为一款简介强大的评论组件,按目前效果来看也仍然有着可观的优化空间。

老朋友 lazyload

Lazyload 在前端性能优化领域已是老生常谈,为了不让一些非关键资源和主要内容争夺带宽和性能,任何媒体资源、DOM 节点、非关键 JS 和 CSS 都可以使用 lazyload 优化。

而 Lazyload 的实现也非常简单,用一个 JavaScript 监控当前页面位置,当元素出现或即将出现在可视区域时才加载资源,而非传统的一上来就把所有资源加载完。

现在是公元 2020 年,还有相当一部分插件绑定 scroll 事件实现 lazyload ,在页面滚动的时候这个函数会被频繁触发,十分浪费性能。相比之下,调用 IntersectionObserver 则是一种更优雅的解决方案,这个 API 会自动「观察」元素是否在可视范围内,早在 Chrome 51 便支持了这一功能,其他现代浏览器也均有支持。除非你真的很在意旧版 IE 用户,不然都建议使用后者。

unalted image

(IntersectionObserver 浏览器支持情况)

Valine ? Yes !

市面上主流的静态博客评论插件其实并不算很多,像是我在使用的 Hexo 主题 Volantis 就适配了 Gitalk, Disqus, Valine 和 Livere 。

可是可能是最老牌好用的 Disqus 已经被墙,尽管有人开发了 DisqusJS 但是需要注册登录才能评论的体验还是有点割裂。 韩国的 Livere 来必力也是需要注册登录才能评论,而且国内访问速度一般,绝对称不上优秀。Gitalk / Gitment 必须要使用 GitHub 账号才能评论,而且受到 GitHub Issue Label 的限制,后端管理也不方便。

最后我还是选择了 Valine ,界面简洁,基于免费版的 LeanCloud 就能轻松跑起,配合 Valine Admin 插件也能获得还说得过去的管理后台。

unalted image

确实,相较于 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);

看来一个外行乱玩代码还是需要点时间来沉淀的呀。

Lazyload 不让 Valine 评论组件拖慢页面加载
本文作者
ChrAlpha
发布日期
2020-03-13
更新日期
2020-06-06
转载或引用本文时请遵守 CC BY-NC-SA 4.0 许可协议,注明出处、不得用于商业用途!
CC BY-NC-SA 4.0