将博客迁移到Hexo之后,抽点时间做了一些优化,现记录下来供同样使用Hexo的朋友一些参考。

0x01 图片懒加载

看了下Hexo官方网站上的几款主题,几乎都是使用jQuery Lazyload这款插件进行图片的懒加载。我记得以前关于这款插件的争论较大,有文章分析说它并未实现真正意义上的延迟加载图片,只是网页滚动到图片区域了将图片显示出来而已。查看了一下官网,发现在新版中已经改善。但是要求我们在插入图片时对img标签做一些修改。

jQuery_Lazyload_howtouse
jQuery_Lazyload_howtouse

如上面的介绍,首先要为img添加data-original属性,然后还需要设置width和height。现在的问题是我已经有很多博文中添加了图片,要我一篇篇去修改我真没有这个耐心,而且我之所以选择Hexo就是为了使用Markdown写博客,而到目前为止标准的Markdown语法连图片的宽高都无法指定,更不说添加别的属性了,虽然Markdown也可以使用img标签。 根据官网的介绍,并查看jQuery Lazyload的源码,我们知道图片懒加载的基本原理应该是:

  • 1.将图片的src属性先设置为一张较小的占位图片,并将真实的图片地址设置为data-original属性的值,中断图片加载。
  • 2.图片滚动到可视区域后再,再把data-original属性的值更改为src属性的值,从而加载图片并显示出来。

所以我们根据这个原理编写Hexo插件,在Hexo生成页面的时候对img标签进行相关的替换即可。我们知道Hexo的插件系统分为脚本(Scripts)和插件(Packages)两种形式,插件(Packages)一般应用于较复杂的功能,或者是想发布到 NPM 上。我们的这个需求较简单,所以编写一个简单的脚本(Scripts)即可。 我们在主题的scripts目录添加一个js文件,文件名可以随意,例如我们命名为lazyload.js,Hexo在启动时会自动载入。下面是我的代码:

'use strict';

var cheerio = require('cheerio');

var cdnUrl = "https://youcdndomain.com";
var loading = "/images/loading.gif";
var oldsrc = '';

function stringStartsWith(string, prefix) {
    return string.slice(0, prefix.length) == prefix;
}

function lazyloadImg(source) {
    var $ = cheerio.load(source, {
        decodeEntities: false
    });
    $('img').each(function(index, element) {
        oldsrc = $(element).attr('src');
        if (oldsrc && !stringStartsWith(oldsrc, baseUrl) && !$(element).hasClass('hx_lazyimg') && !$(element).hasClass('skip')) {
            $(element).addClass('hx_lazyimg');
            $(element).attr({
                src: loading,
                'data-original': cdnUrl + oldsrc
            });
        }
    });
    return $.html();
}

hexo.extend.filter.register('after_render:html', lazyloadImg);

注意将代码中的cdnUrl修改为你自己的图片cdn地址,将loading修改为你自己的占位图片地址。上面的代码不仅修改了图片的属性做懒加载,顺便也替换了图片的地址为CDN的地址,一举两得。这样我们在Markdown中添加图片时依然使用我们自己的图片地址,避免之后更换CDN空间时又要批量修改图片地址的麻烦。

另外其实我并没有使用jQuey Lazyload,而是参考之前在Wordpress上使用的Simple Lazyload插件进行的修改。但是我测试了同样也可以适用于jQuey Lazyload,只是你需要引入相关的js文件,然后调用。

<script src="jquery.js"></script>
<script src="jquery.lazyload.js"></script>


$(function() {
    $("img.hx_lazyimg").lazyload();
});

顺便贴出我从Simple Lazyload中提取出的javascript代码,如果你不想使用jQuey Lazyload,可以复制下面的代码保存为js文件,然后在主题中引入即可。

Array.prototype.S = String.fromCharCode(2);
Array.prototype.in_array = function(e) {
    var r = new RegExp(this.S + e + this.S);
    return (r.test(this.S + this.join(this.S) + this.S));
};

Array.prototype.pull = function(content) {
    for (var i = 0, n = 0; i < this.length; i++) {
        if (this[i] != content) {
            this[n++] = this[i];
        }
    }
    this.length -= 1;
};

jQuery(function($) {
    window._lazyimgs = $("img.hx_lazyimg");
    if (_lazyimgs.length == 0) {
        return;
    }
    var toload_inds = [];
    var loaded_inds = [];
    var failed_inds = [];
    var failed_count = {};
    var lazyload = function() {
        if (loaded_inds.length == _lazyimgs.length) {
            return;
        }
        var threshold = 200;
        var attrName = 'data-original'
        _lazyimgs.each(function(i) {
            _self = $(this);
            if (_self.attr("lazyloadpass") === undefined && _self.attr(attrName) && (!_self.attr("src") || (_self.attr("src") && _self.attr(attrName) != _self.attr("src")))) {
                if ((_self.offset().top) < ($(window).height() + $(document).scrollTop() + threshold) && (_self.offset().left) < ($(window).width() + $(document).scrollLeft() + threshold) && (_self.offset().top) > ($(document).scrollTop() - threshold) && (_self.offset().left) > ($(document).scrollLeft() - threshold)) {
                    if (toload_inds.in_array(i)) {
                        return;
                    }
                    toload_inds.push(i);
                    if (failed_count["count" + i] === undefined) {
                        failed_count["count" + i] = 0;
                    }
                    _self.css("opacity", 1);
                    $("<img ind=\"" + i + "\"/>").bind("load", function() {
                        var ind = $(this).attr("ind");
                        if (loaded_inds.in_array(ind)) {
                            return;
                        }
                        loaded_inds.push(ind);
                        var _img = _lazyimgs.eq(ind);
                        _img.attr("src", _img.attr(attrName)).css("background-image", "none").attr("lazyloadpass", "1");
                    }).bind("error", function() {
                        var ind = $(this).attr("ind");
                        if (!failed_inds.in_array(ind)) {
                            failed_inds.push(ind);
                        }
                        failed_count["count" + ind]++;
                        if (failed_count["count" + ind] < 2) {
                            toload_inds.pull(ind);
                        }
                    }).attr("src", _self.attr(attrName));
                }
            }
        });
    }
    lazyload();
    var ins;
    $(window).scroll(function() {
        clearTimeout(ins);
        ins = setTimeout(lazyload, 100);
    });
    $(window).resize(function() {
        clearTimeout(ins);
        ins = setTimeout(lazyload, 100);
    });
});

jQuery(function($) {
    var calc_image_height = function(_img) {
        var width = _img.attr("width");
        var height = _img.attr("height");
        if (!(width && height && width >= 300)) return;
        var now_width = _img.width();
        var now_height = parseInt(height * (now_width / width));
        _img.css("height", now_height);
    }
    var fix_images_height = function() {
        _lazyimgs.each(function() {
            calc_image_height($(this));
        });
    }
    fix_images_height();
    $(window).resize(fix_images_height);
});

0x02 使用免费CDN托管静态资源

我们写博客总免不了会添加一些图片,而且不可避免的总会使用的一些css样式和javascript文件。我们可以将这些静态资源放置在一些免费的CDN空间上,比如七牛又拍云阿里百川等。这些厂商都提供一些免费的空间和流量,对于我们一个博客来说已经足够使用了。 你需要做的只是注册帐号,将资源上传到CDN空间,然后修改主题中的链接地址即可。对于css,js等因为资源较少,可以手动修改。对于图片地址,在上面介绍图片懒加载时已经做了说明,如果不明确的地方,请留言。