从放飞自我到癫狂——LazyLoad魔改版的降生

之前配置服务器的时候不知道哪根筋搭错了,非要想在nginx原版里面把请求合并给弄进去,终于在不知道什么原因的作用下达成了把软件搞崩的结果。

目前的情况就是请求数不管怎么配置,总是会出现503。看了服务器自带的防CC,显然不太可能是它出手了。

排查了一个多月以后,我彻底摆烂了,对自己的又菜又爱玩有了更深刻的认知。

在放飞自我了一段时间后,突然想到请求数大部分是来自于图片,如果能让图片不在同一时间向服务器发起请求,那么不就基本能减少503吗(好吧,实际上还是会莫名其妙有503,就当玄学看了)?

好吧,说干就干。

先对正文图片的格式进行改造。

初始格式:

<img src="此处为图片的实际url"/>

改造后的格式:

<img src="data:image/gif;base64,R0lGODdhAQABAPAAAMPDwwAAACwAAAAAAQABAAACAkQBADs=" class="lazyload" data-src="此处为图片的实际url" />

在页面引用的js文件中加入以下魔改后的代码(为了不影响页面加载,可以放在body的尾部某个位置):

/** Lazy Load https://appelsiini.net/projects/lazyload Version: 2.0.0-rc.2 * */

(function(root, factory) {
    if (typeof exports === "object") {
        module.exports = factory(root);
    } else if (typeof define === "function" && define.amd) {
        define([], factory);
    } else {
        root.LazyLoad = factory(root);
    }
}
)(typeof global !== "undefined" ? global : this.window || this.global, function(root) {

    "use strict";

    if (typeof define === "function" && define.amd) {
        root = window;
    }

    const defaults = {
        src: "data-src",
        srcset: "data-srcset",
        selector: ".lazyload",
        root: null,
        rootMargin: "0px",
        threshold: 0,
        delay: 200 // 默认延迟200毫秒
    };

    const extend = function() {
        let extended = {};
        let deep = false;
        let i = 0;
        let length = arguments.length;
        if (Object.prototype.toString.call(arguments[0]) === "[object Boolean]") {
            deep = arguments[0];
            i++;
        }
        let merge = function(obj) {
            for (let prop in obj) {
                if (Object.prototype.hasOwnProperty.call(obj, prop)) {
                    if (deep && Object.prototype.toString.call(obj[prop]) === "[object Object]") {
                        extended[prop] = extend(true, extended[prop], obj[prop]);
                    } else {
                        extended[prop] = obj[prop];
                    }
                }
            }
        };
        for (; i < length; i++) {
            let obj = arguments[i];
            merge(obj);
        }
        return extended;
    };

    function LazyLoad(images, options) {
        this.settings = extend(defaults, options || {});
        this.images = images || document.querySelectorAll(this.settings.selector);
        this.observer = null;
        this.queue = [];
        // 新增队列
        this.isProcessing = false;
        // 是否正在处理队列
        this.lastLoadTime = 0;
        // 记录上一次加载完成的时间
        this.init();
    }

    LazyLoad.prototype = {
        init: function() {
            if (!root.IntersectionObserver) {
                this.loadImages();
                return;
            }

            let self = this;
            let observerConfig = {
                root: this.settings.root,
                rootMargin: this.settings.rootMargin,
                threshold: [this.settings.threshold]
            };

            this.observer = new IntersectionObserver(function(entries) {
                Array.prototype.forEach.call(entries, function(entry) {
                    if (entry.isIntersecting) {
                        self.observer.unobserve(entry.target);

                        // 将图片加入队列
                        self.queue.push(entry.target);

                        // 如果未在处理队列,则开始处理
                        if (!self.isProcessing) {
                            self.processQueue();
                        }
                    }
                });
            }
            ,observerConfig);

            Array.prototype.forEach.call(this.images, function(image) {
                self.observer.observe(image);
            });
        },

        processQueue: function() {
            if (this.queue.length === 0) {
                this.isProcessing = false;
                return;
            }

            this.isProcessing = true;

            const now = Date.now();
            const timeSinceLastLoad = now - this.lastLoadTime;

            // 计算需要等待的时间
            const waitTime = Math.max(0, this.settings.delay - timeSinceLastLoad);

            setTimeout( () => {
                // 从队列中取出第一张图片
                const image = this.queue.shift();
                const src = image.getAttribute(this.settings.src);
                const srcset = image.getAttribute(this.settings.srcset);

                if ("img" === image.tagName.toLowerCase()) {
                    if (src) {
                        image.src = src;
                    }
                    if (srcset) {
                        image.srcset = srcset;
                    }
                } else {
                    image.style.backgroundImage = "url(" + src + ")";
                }

                // 更新最后加载完成时间
                this.lastLoadTime = Date.now();

                // 处理下一张图片
                this.processQueue();
            }
            , waitTime);
        },

        loadAndDestroy: function() {
            if (!this.settings) {
                return;
            }
            this.loadImages();
            this.destroy();
        },

        loadImages: function() {
            if (!this.settings) {
                return;
            }
            let self = this;
            Array.prototype.forEach.call(this.images, function(image) {
                let src = image.getAttribute(self.settings.src);
                let srcset = image.getAttribute(self.settings.srcset);
                if ("img" === image.tagName.toLowerCase()) {
                    if (src) {
                        image.src = src;
                    }
                    if (srcset) {
                        image.srcset = srcset;
                    }
                } else {
                    image.style.backgroundImage = "url('" + src + "')";
                }
            });
        },

        destroy: function() {
            if (!this.settings) {
                return;
            }
            this.observer.disconnect();
            this.settings = null;
        }
    };

    root.lazyload = function(images, options) {
        return new LazyLoad(images,options);
    }
    ;

    if (root.jQuery) {
        const $ = root.jQuery;
        $.fn.lazyload = function(options) {
            options = options || {};
            options.attribute = options.attribute || "data-src";
            new LazyLoad($.makeArray(this),options);
            return this;
        }
        ;
    }

    return LazyLoad;
});

记得要插在jquery后面。

在需要使用懒加载的页面插入以下代码(以下代码要保证在:

 $('img.lazyload').lazyload({ delay: 400 });

dely后面的参数是上一张图片开始加载后多少毫秒开始加载下一张图片(原则上是上一张加载完成后开始计时,不过实际上嘛……反正差不多能用就行了,哈哈哈,不要太在意细节)。

对了,整个逻辑就是把可视区域class="lazyload"的图片排序,按照先后逐步加载。

非常感谢大佬的原始代码。

lazyload官网

标签:

作者头像
老姚创始人

差点成为有力量的石油工人的世界上最水的财务经理。

上一篇:《哪吒之魔童闹海》破百亿,电影圈真要变天了!
下一篇:变名记:一个网站的中年危机

发表评论