lazyLoad与节流

来源:互联网 发布:2017淘宝如何投诉盗图 编辑:程序博客网 时间:2024/05/16 01:35

原理

对于图片过多的页面,为了加速页面加载速度,所以很多时候我们需要将页面内未出现在可视区域内的图片先不做加载, 等到滚动到可视区域后再去加载。这样子对于页面加载性能上会有很大的提升,也提高了用户体验。
将页面中的img标签src指向一张小图片或者src为空,然后定义data-src属性指向真实的图片。src指向一张默认的图片,否则当src为空时也会向服务器发送一次请求。可以指向loading图的地址。
当载入页面时,先把可视区域内的img标签的data-src属性值赋给src,然后监听滚动事件,把用户即将看到的图片加载。这样便实现了懒加载。

实现

实现比较简单,直接上代码

   var num = document.getElementsByTagName('img').length;    var img = document.getElementsByTagName("img");    //存储图片加载到的位置,避免每次都从第一张图片开始遍历    var n = 0;     //页面载入完毕加载可视区域内的图片    lazyLoad();     window.onscroll = lazyload;    function lazyLoad(){        //可见区域高度        var seeHeight = window.innerHeight || document.documentElementHeight || document.body.clientHeight;        //滚动条距离顶部高度        var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;         for(var i = n; i < num; i++){            if(img[i].offsetHeight < scrollTop + seeHeight){                if (img[i].getAttribute("src") == "default.png") {                    img[i].src = img[i].getAttribute("data-src");                }                n = i + 1;            }        }    }

这里的判断也可以这样写

   //元素上边距离页面上边的距离   var imgTop = img[i].getBoundingClientRect().top;   if(imgTop < seeHeight){       //lazyload...   }

使用节流函数进行性能优化

如果直接将函数绑定在scroll事件上,当页面滚动时,函数会被高频触发,这非常影响浏览器的性能。
节流函数:只允许一个函数在N秒内执行一次。下面是一个简单的节流函数

    var COUNT = 0;    function testFn() { console.log(COUNT++); }    // 浏览器resize的时候    // 1. 清除之前的计时器    // 2. 添加一个计时器让真正的函数testFn延后100毫秒触发    // 等不再resize的时候过100ms才触发    window.onresize = function(){        var timer = null;        clearTimeout(timer);        timer = setTimeout(function(){            testFn();        }, 100);    };

细心的同学会发现上面的代码其实是错误的:setTimeout 函数返回值应该保存在一个相对全局变量里面,否则每次 resize 的时候都会产生一个新的计时器(在任务队列中,每次独立执行),这样就达不到我们发的效果了

    var timer = null;    window.onresize = function(){        clearTimeout(timer);        timer = setTimeout(function(){            testFn();        }, 100);    };

这时候代码就正常了,但是又多了一个新问题 —— 产生了一个全局变量 timer。这是我们不想见到的,如果这个页面还有别的功能也叫 timer 不同的代码之前就是产生冲突。为了解决这个问题我们要用js的一个语言特性:闭包

   var throttle = function (fn, delay) {        var timer = null;        return function () {            clearTimeout(timer);            timer = setTimeout(function() {                fn();            }, delay);        }    };    window.onresize = throttle(testFn, 200);

但是现在又有一个问题:如果用户不断的 resize 浏览器窗口大小,这时延迟处理函数一次都不会执行。于是我们又要添加一个功能:当用户触发 resize 的时候应该 在某段时间 内至少触发一次,既然是在某段时间内,那么这个判断条件就可以取当前的时间毫秒数,每次函数调用把当前的时间和上一次调用时间相减,然后判断差值如果大于 某段时间 就直接触发,否则还是走 timeout 的延迟逻辑。

    var throttle = function (fn, delay, atleast) {        var timer = null;        var previous = null;        return function () {            var now = +new Date();              if (!previous) previous = now;            if (now - previous > atleast) {                fn();                // 重置上一次开始时间为本次结束时间                previous = now;            } else {                clearTimeout(timer);                timer = setTimeout(function() {                    fn();                }, delay);            }        }    };    window.onresize = throttle(testFn, 200, 1000);

最后我们来实现一下我们的lazyLoad函数

   function throttle(fn, delay, time) {        var timeout,            previous = new Date();        return function() {            var context = this,                args = arguments,                now = new Date();            clearTimeout(timeout);            // 如果达到了规定的触发时间间隔,触发 handler            if (now - previous >= time) {                //采用函数内部的this(非严格模式下是window),避免this被DOM元素绑定                fn.apply(context, args);                previous = now;                // 没达到触发间隔,重新设定定时器            } else {                timeout = setTimeout(fn, delay);            }        };    };    function lazyload(event){        //...    }    // 采用了节流函数    window.addEventListener('scroll', throttle(lazyload, 500, 1000));
0 0
原创粉丝点击