详解懒加载和预加载(js)

时间: 05/29/2019作者: ll浏览量: 12481

1. 懒加载

1.1 是什么懒加载?

懒加载也就是延迟加载。 当访问一个页面的时候,先把img元素或是其他元素的背景图片路径替换成一张大小为1*1px图片的路径(这样就只需请求一次,俗称占位图),只有当图片出现在浏览器的可视区域内时,才设置图片正真的路径,让图片显示出来。这就是图片懒加载。

1.2 为什么要使用懒加载?

很多页面,内容很丰富,页面很长,图片较多。比如说各种商城页面。这些页面图片数量多,而且比较大,少说百来K,多则上兆。要是页面载入就一次性加载完毕。估计大家都会等到黄花变成黄花菜了。

1.3 懒加载的原理是什么?

页面中的img元素,如果没有src属性,浏览器就不会发出请求去下载图片,只有通过javascript设置了图片路径,浏览器才会发送请求。 懒加载的原理就是先在页面中把所有的图片统一使用一张占位图进行占位,把正真的路径存在元素的“data-url”(这个名字起个自己认识好记的就行)属性里,要用的时候就取出来,再设置;

1.4 懒加载的实现步骤?

1)首先,不要将图片地址放到src属性中,而是放到其它属性(data-original)中。 2)页面加载完成后,根据scrollTop判断图片是否在用户的视野内,如果在,则将data-original属性中的值取出存放到src属性中。 3)在滚动事件中重复判断图片是否进入视野,如果进入,则将data-original属性中的值取出存放到src属性中。

1.5 懒加载的优点是什么?

页面加载速度快、可以减轻服务器的压力、节约了流量,用户体验好

1.6 js代码实现

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0">
<title>lazyLoad</title>
<style type="text/css" media="all">
body,ul,li { padding:0; margin:0; }
body { font-size:12px; -webkit-user-select:none; -webkit-text-size-adjust:none; font-family:helvetica;}
.lazyLoad ul li {height:200px; border:1px solid red; margin-bottom:2px; overflow: hidden; text-align:center;}
</style>
</head>
<body>
    <div class="lazyLoad">
        <ul>
            <li><span data-src="https://www.geekjc.com">我不是图片啦</span></li>
            <li><img data-src="http://qiniu.cllgeek.com/react02.png" alt=""></li>
            <li><img data-src="http://qiniu.cllgeek.com/react03.png" alt=""></li>
            <li><img data-src="http://qiniu.cllgeek.com/react04.png" alt=""></li>
            <li><img data-src="http://qiniu.cllgeek.com/react05.png" alt=""></li>
            <li><img data-src="http://qiniu.cllgeek.com/react06.png" alt=""></li>
            <li><img data-src="http://qiniu.cllgeek.com/react07.png" alt=""></li>
            <li><img data-src="http://qiniu.cllgeek.com/react08.png" alt=""></li>
            <li><img load-src="http://qiniu.cllgeek.com/react13.png" alt=""></li>
        </ul>
    </div>

    <script>
        var loadPic = function(obj,option){
            if(!obj) return;
            return new lazyLoad(obj,option);
        }

        function lazyLoad(){
            this.init.apply(this,arguments);
        }

        lazyLoad.prototype = {
            init : function(el,param){
                var opt = {
                    dataSrc : 'lazyImg',
                    dataImg : 'http://qiniu.cllgeek.com/loading.gif',
                    contens : typeof el === 'string' ? this.getElement(el) : el || document.body,
                    cb : null
                }

                if(param && param instanceof Object){
                    for(var key in param){
                        if(param.hasOwnProperty(key)){
                            opt[key] = param[key];
                        }
                    }
                }
                console.log(opt)
                this.opt = opt;
                this.render();
                this.bindUI();
            },
            render : function(){
                var _opt = this.opt,
                    _imgArr = [],
                    _dom = _opt.contens,
                    _img = _dom.querySelectorAll('['+_opt.dataSrc+']');

                if(_img.length){
                    var _tagName;
                    Array.prototype.forEach.call(_img,function(item,i){
                        _tagName = item.tagName.toLowerCase();
                        if(_tagName === 'img' && !item.getAttribute('src')){
                            item.setAttribute('src',this.opt.dataImg);
                        }
                        _imgArr.push({
                            obj : item,
                            tagName : item.tagName.toLowerCase(),
                            src : item.getAttribute(_opt.dataSrc)
                        })
                    }.bind(this));
                }
                this.imgArr = _imgArr;
                this.loadImg();
            },
            bindUI : function(){
                window.addEventListener('scroll',function(e){
                    this.loadImg();
                }.bind(this),false);
            },
            loadImg : function(){
                // console.log(this.imgArr);
                var _imgArr = this.imgArr;
                var _dataSrc, _src;
                if(_imgArr.length){
                    _imgArr.forEach(function(item,i){
                        if(this.isLoad(item.obj)){
                            //如果是图片
                            if(item.tagName == 'img'){
                                _dataSrc = this.opt.dataSrc;
                                _src = item.obj.getAttribute(_dataSrc);
                                if(_src){

                                    item.obj.setAttribute('src',_src);
                                    item.obj.removeAttribute(_dataSrc);

                                    if(typeof this.opt.cb === 'function'){
                                        return this.opt.cb.call(this,item.obj)
                                    }
                                }
                            }else{
                                //加载文件
                                if(typeof this.opt.cb === 'function'){
                                    if(_imgArr[i].isLoad){
                                        return false;
                                    }
                                    _imgArr[i].isLoad = 1;
                                    return this.opt.cb.call(this,item.obj)
                                }
                            }
                        }
                    }.bind(this));
                }
            },
            isLoad : function(img){
                var _scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
                var _winHeight = window.innerHeight;
                var _offsetTop = img.offsetTop;
                var _height = img.offsetHeight
                if(_offsetTop + _height >= _scrollTop && _offsetTop <= _winHeight + _scrollTop){
                    return true;
                }
                return false;
            },
            getElement : function(o){
                return document.querySelector(o);
            }
        }

        var t = loadPic('.lazyLoad',{
            dataSrc : 'data-src',
            cb : function(item){
                console.log(item)
            }
        })
    </script>
</body>
</html>

2. 预加载

2.1 什么是预加载

提前加载图片,当用户需要查看时可直接从本地缓存中渲染

2.2 为什么要使用预加载?

图片预先加载到浏览器中,访问者便可顺利地在你的网站上冲浪,并享受到极快的加载速度。这对图片画廊及图片占据很大比例的网站来说十分有利,它保证了图片快速、无缝地发布,也可帮助用户在浏览你网站内容时获得更好的用户体验。

2.3 实现预加载的方法有哪些?

方法一:用CSS和JavaScript实现预加载 单纯使用CSS,可容易、高效地预加载图片,代码如下:

#preload-01 { background: url(http://qiniu.cllgeek.com/react02.png) no-repeat -9999px -9999px; }  
#preload-02 { background: url(http://qiniu.cllgeek.com/react03.png) no-repeat -9999px -9999px; }  
#preload-03 { background: url(http://qiniu.cllgeek.com/react04.png no-repeat -9999px -9999px; }

将这三个ID选择器应用到(X)HTML元素中,我们便可通过CSS的background属性将图片预加载到屏幕外的背景上。只要这些图片的路径保持不变,当它们在Web页面的其他地方被调用时,浏览器就会在渲染过程中使用预加载(缓存)的图片。简单、高效,不需要任何JavaScript。

该方法虽然高效,但仍有改进余地。使用该法加载的图片会同页面的其他内容一起加载,增加了页面的整体加载时间。为了解决这个问题,我们增加了一些JavaScript代码,来推迟预加载的时间,直到页面加载完毕。代码如下:

// better image preloading @ <a href="http://perishablepress.com/press/2009/12/28/3-ways-preload-images-css-javascript-ajax/">http://perishablepress.com/press/2009/12/28/3-ways-preload-images-css-javascript-ajax/</a>  
function preloader() {  
    if (document.getElementById) {  
        document.getElementById("preload-01").style.background = "url(http://qiniu.cllgeek.com/react02.png) no-repeat -9999px -9999px";  
        document.getElementById("preload-02").style.background = "url(http://qiniu.cllgeek.com/react03.png) no-repeat -9999px -9999px";  
        document.getElementById("preload-03").style.background = "url(http://qiniu.cllgeek.com/react04.png) no-repeat -9999px -9999px";  
    }  
}  
function addLoadEvent(func) {  
    var oldonload = window.onload;  
    if (typeof window.onload != 'function') {  
        window.onload = func;  
    } else {  
        window.onload = function() {  
            if (oldonload) {  
                oldonload();  
            }  
            func();  
        }  
    }  
}  
addLoadEvent(preloader);

在该脚本的第一部分,我们获取使用类选择器的元素,并为其设置了background属性,以预加载不同的图片。

该脚本的第二部分,我们使用addLoadEvent()函数来延迟preloader()函数的加载时间,直到页面加载完毕。

如果JavaScript无法在用户的浏览器中正常运行,会发生什么?很简单,图片不会被预加载,当页面调用图片时,正常显示即可。

方法二:仅使用javascript实现预加载 上述方法有时确实很高效,但我们逐渐发现它在实际实现过程中会耗费太多时间。相反,我更喜欢使用纯JavaScript来实现图片的预加载。下面将提供两种这样的预加载方法,它们可以很漂亮地工作于所有现代浏览器之上。

javascript代码段1

只需简单编辑、加载所需要图片的路径与名称即可,很容易实现:

<div class="hidden">  
    <script type="text/javascript">  
        <!--//--><![CDATA[//><!--  
            var images = new Array()  
            function preload() {  
                for (i = 0; i < preload.arguments.length; i++) {  
                    images[i] = new Image()  
                    images[i].src = preload.arguments[i]  
                }  
            }  
            preload(
                "http://qiniu.cllgeek.com/react02.png",
                "http://qiniu.cllgeek.com/react03.png",
                "http://qiniu.cllgeek.com/react04.png"
            ) 
        //--><!]]>  
    </script>  
</div>

该方法尤其适用预加载大量的图片。我的画廊网站使用该技术,预加载图片数量达50多张。将该脚本应用到登录页面,只要用户输入登录帐号,大部分画廊图片将被预加载。

javascript代码段2

该方法与上面的方法类似,也可以预加载任意数量的图片。将下面的脚本添加入任何Web页中,根据程序指令进行编辑即可。

<div class="hidden">  
    <script type="text/javascript">  
        <!--//--><![CDATA[//><!--  
            if (document.images) {  
                img1 = new Image();  
                img2 = new Image();  
                img3 = new Image();  
                img1.src = "http://qiniu.cllgeek.com/react02.png";  
                img2.src = "http://qiniu.cllgeek.com/react03.png";  
                img3.src = "http://qiniu.cllgeek.com/react04.png";  
            }  
        //--><!]]>  
    </script>  
</div>

正如所看见,每加载一个图片都需要创建一个变量,如“img1 = new Image();”,及图片源地址声明,如“img3.src = “../path/to/image-003.gif”;”。参考该模式,你可根据需要加载任意多的图片。

我们又对该方法进行了改进。将该脚本封装入一个函数中,并使用 addLoadEvent(),延迟预加载时间,直到页面加载完毕。

function preloader() {  
    if (document.images) {  
        var img1 = new Image();  
        var img2 = new Image();  
        var img3 = new Image();  
        img1.src = "http://qiniu.cllgeek.com/react02.png";  
        img2.src = "http://qiniu.cllgeek.com/react03.png";  
        img3.src = "http://qiniu.cllgeek.com/react04.png";  
    }  
}  
function addLoadEvent(func) {  
    var oldonload = window.onload;  
    if (typeof window.onload != 'function') {  
        window.onload = func;  
    } else {  
        window.onload = function() {  
            if (oldonload) {  
                oldonload();  
            }  
            func();  
        }  
    }  
}  
addLoadEvent(preloader);

方法三:使用Ajax实现预加载 上面所给出的方法似乎不够酷,那现在来看一个使用Ajax实现图片预加载的方法。该方法利用DOM,不仅仅预加载图片,还会预加载CSS、JavaScript等相关的东西。使用Ajax,比直接使用JavaScript,优越之处在于JavaScript和CSS的加载不会影响到当前页面。该方法简洁、高效。

window.onload = function() {  
    setTimeout(function() {  
        // XHR to request a JS and a CSS  
        var xhr = new XMLHttpRequest();  
        xhr.open('GET', 'http://geekjc.com/preload.js');  
        xhr.send('');  
        xhr = new XMLHttpRequest();  
        xhr.open('GET', 'http://geekjc.com/preload.css');  
        xhr.send('');  
        // preload image  
        new Image().src = "http://geekjc.com/preload.png";  
    }, 1000);  
};

上面代码预加载了“preload.js”、“preload.css”和“preload.png”。1000毫秒的超时是为了防止脚本挂起,而导致正常页面出现功能问题。

下面,我们看看如何用JavaScript来实现该加载过程:

window.onload = function() {  

    setTimeout(function() {  

        // reference to <head>  
        var head = document.getElementsByTagName('head')[0];  

        // a new CSS  
        var css = document.createElement('link');  
        css.type = "text/css";  
        css.rel  = "stylesheet";  
        css.href = "http://geekjc.com/preload.css";  

        // a new JS  
        var js  = document.createElement("script");  
        js.type = "text/javascript";  
        js.src  = "http://geekjc.com/preload.js";  

        // preload JS and CSS  
        head.appendChild(css);  
        head.appendChild(js);  

        // preload image  
        new Image().src = "http://geekjc.com/preload.png";  

    }, 1000);  

};

这里,我们通过DOM创建三个元素来实现三个文件的预加载。正如上面提到的那样,使用Ajax,加载文件不会应用到加载页面上。从这点上看,Ajax方法优越于JavaScript。

3. 懒加载和预加载的对比

1)概念: 懒加载也叫延迟加载:JS图片延迟加载,延迟加载图片或符合某些条件时才加载某些图片。 预加载:提前加载图片,当用户需要查看时可直接从本地缓存中渲染。

2)区别: 两种技术的本质:两者的行为是相反的,一个是提前加载,一个是迟缓甚至不加载。懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力。

3)懒加载的意义及实现方式有: 意义: 懒加载的主要目的是作为服务器前端的优化,减少请求数或延迟请求数。 实现方式:

  1. 第一种是纯粹的延迟加载,使用setTimeOut或setInterval进行加载延迟.
  2. 第二种是条件加载,符合某些条件,或触发了某些事件才开始异步下载。
  3. 第三种是可视区加载,即仅加载用户可以看到的区域,这个主要由监控滚动条来实现,一般会在距用户看到某图片前一定距离遍开始加载,这样能保证用户拉下时正好能看到图片。

4)预加载的意义及实现方式有: 意义: 预加载可以说是牺牲服务器前端性能,换取更好的用户体验,这样可以使用户的操作得到最快的反映。 实现方式: 实现预载的方法非常多,比如:用CSS和JavaScript实现预加载;仅使用JavaScript实现预加载;使用Ajax实现预加载。 常用的是new Image();设置其src来实现预载,再使用onload方法回调预载完成事件。只要浏览器把图片下载到本地,同样的src就会使用缓存,这是最基本也是最实用的预载方法。当Image下载完图片头后,会得到宽和高,因此可以在预载前得到图片的大小(方法是用记时器轮循宽高变化)。

4. 补充知识

  1. 屏幕可视窗口大小

     **原生方法**:
         window.innerHeight 标准浏览器及IE9+ ||
         document.documentElement.clientHeight 标准浏览器及低版本IE标准模式 ||
         document.body.clientHeight  低版本混杂模式
     **jQuery方法**: 
         $(window).height();
    
  2. 浏览器窗口顶部与文档顶部之间的距离,也就是滚动条滚动的距离:

     **原生方法**:
           window.pagYoffset 标准浏览器及IE9+ ||
           document.documentElement.scrollTop 兼容ie低版本的标准模式 ||
           document.body.scrollTop 兼容混杂模式;
     **jQuery方法**:
           $(document).scrollTop();
    
  3. 获取元素的尺寸

    $(o).width() = o.style.width;
    $(o).innerWidth() = o.style.width+o.style.padding;
    $(o).outerWidth() = o.offsetWidth = o.style.width+o.style.padding+o.style.border;
    $(o).outerWidth(true) = o.style.width+o.style.padding+o.style.border+o.style.margin;
    

    注意 要使用原生的style.xxx方法获取属性,这个元素必须已经有内嵌的样式,如<div style="...."></div>; 如果原先是通过外部或内部样式表定义css样式,必须使用o.currentStyle[xxx] || document.defaultView.getComputedStyle(0)[xxx]来获取样式值。

  4. 获取元素的位置信息 jQuery: $(o).offset().top元素距离文档顶的距离 $(o).offset().left元素距离文档左边缘的距离。 原生:getoffsetTop(); 顺便提一下返回元素相对于第一个以定位的父元素的偏移距离,注意与上面偏移距的区别; jQuery:position()返回一个对象 $(o).position().left = o.style.left; $(o).position().top = o.style.top;

关注下面的标签,发现更多相似的文章

ll

前端工程师
自我介绍 专注web前后端架构,极客教程网(https://www.geekjc.com)-创建者。
极客教程网
——一个你值得来的网站
编程笔记:学习分享,与君共勉!
小书本:系统完整的学习!
在线工具:极客工具,在线工具,在线运行
前端导航:前端导航,前端资源聚合平台
零花钱:简单点点手机,做做任务赚点零花钱
微信公众号
极客教程(geekjc)
QQ群:
495489065

Copyright © 2019 - ~ All Rights Reserved. Made By ll 备案号:粤ICP备15001588号-4