Skip to content

Latest commit

 

History

History
149 lines (121 loc) · 7.25 KB

3.16-LazyLoad.md

File metadata and controls

149 lines (121 loc) · 7.25 KB

懒加载

参考:

什么是懒加载(Lazy Load)

懒加载,也可以叫延迟加载、惰性加载、按需加载。图片懒加载就是在长网页中延迟加载图像:用户滚动到它们之前,视口外的图像不会加载。

举个例子来说明,当打开淘宝首页的时候,只有在浏览器窗口里的图片才会被加载,当你滚动首页向下滑的时候,进入视口内的图片才会被加载,而其它从未进入视口的图像不会被加载。

图片懒加载

为什么要使用懒加载?

  1. 使网页加载更快,用户体验更好
  2. 可以帮助减少并发请求,减轻服务器负载。

实现方式

基本原理:先把img元素或是其他元素的背景图片路径设置为空或同一张图片(这样只用请求一次),而页面中的img元素如果没有src属性就不会发出请求去下载图片,只有当图片出现在浏览器的可视区域内时,才设置图片真正的路径,浏览器才会发出请求,让图片显示出来。

为了实现懒加载,我们需要先了解一些基本的知识,比如说如何获取某个元素的尺寸大小、滚动条滚动距离及偏移位置距离,如下图。

位置图

  • 屏幕可视窗口大小:对应于图中1、2位置处
    • window.innerHeight 标准浏览器及IE9+
    • document.documentElement.clientHeight 标准浏览器及低版本IE标准模式
    • document.body.clientHeight 低版本混杂模式
  • 浏览器窗口顶部与文档顶部之间的距离,也就是滚动条滚动的距离:也就是图中3、4处对应的位置
    • window.pagYoffset——IE9+及标准浏览器
    • document.documentElement.scrollTop 兼容ie低版本的标准模式
    • document.body.scrollTop 兼容混杂模式;
  • 获取元素的尺寸:对应于图中5、6位置处
    • el.style.width + el.style.padding + el.style.border + el.style.margin (使用style属性,这个元素必须已经有内嵌的样式,如<div style="...."></div>)
  • 获取元素的位置信息:对应与图中7、8位置处
    • 返回元素相对于文档document顶部、左边的距离;
      • getoffsetTop()

下面介绍几种实现方式:

  1. 页面滚动的时候计算图片的位置与滚动的位置,当图片出现在浏览器视口内时,将图片的 src 属性设置为 data-src 的值,这样,就可以实现延迟加载。

比较 image 的 offsetTop 与 seeHeight + scrollTop 的大小,当小于时则说明图片已经出现过在视口中,这时候继续判断图片是否已经替换过,如果没有替换过,则进行替换。

 function lazyload() {
     var images = document.getElementsByTagName('img');
     var len    = images.length;
     var n      = 0;      //存储图片加载到的位置,避免每次都从第一张图片开始遍历
     return function() {
        var seeHeight = document.documentElement.clientHeight; //可视区域的高度
        var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; //滚动条位置
        for(var i = n; i < len; i++) {
            if(images[i].offsetTop < seeHeight + scrollTop) { //offsetTop返回当前元素相对于其 offsetParent 元素的顶部的距离。
                if(images[i].getAttribute('src') === 'images/loading.gif') {
                 images[i].src = images[i].getAttribute('data-src');
                }
                n++;
            }
        }
     }
 }
 var loadImages = lazyload();
 loadImages();          //初始化首页的页面图片
 window.addEventListener('scroll', loadImages, false);

demo1

这种方法直接将函数绑定在 scroll 事件上,当页面滚动时,函数会被高频触发,这非常影响浏览器的性能。为了改善性能,接下来我们介绍优化后的第二种方法

  1. 在做事件绑定的时候,对 lazyload 函数进行函数节流(throttle)与函数去抖(debounce)处理。

简单来说:

  • Debounce:一部电梯停在某一个楼层,当有一个人进来后,20秒后自动关门,这20秒的等待期间,又一个人按了电梯进来,这20秒又重新计算,直到电梯关门那一刻才算是响应了事件。
  • Throttle:好比一台自动的饮料机,按拿铁按钮,在出饮料的过程中,不管按多少这个按钮,都不会连续出饮料,中间按钮的响应会被忽略,必须要等这一杯的容量全部出完之后,再按拿铁按钮才会出下一杯。

下面就是经过 throttle 处理后的代码:

function throttle(fn, delay, atleast) {
    var timeout = null,
    startTime = new Date();
    return function() {
    var curTime = new Date();
    clearTimeout(timeout);
    if(curTime - startTime >= atleast) {
        fn();
        startTime = curTime;
    }else {
        timeout = setTimeout(fn, delay);
    }
    }
}
function lazyload() {
    var images = document.getElementsByTagName('img');
    var len    = images.length;
    var n      = 0;      //存储图片加载到的位置,避免每次都从第一张图片开始遍历		
    return function() {
    var seeHeight = document.documentElement.clientHeight;
    var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
    for(var i = n; i < len; i++) {
        if(images[i].offsetTop < seeHeight + scrollTop) {
            if(images[i].getAttribute('src') === 'images/loading.gif') {
             images[i].src = images[i].getAttribute('data-src');
            }
            n = n + 1;
         }
    }
    }
}
var loadImages = lazyload();
loadImages();          //初始化首页的页面图片
window.addEventListener('scroll', throttle(loadImages, 500, 1000), false);

demo2 可以很明显的看出有一定延迟

  1. 使用 IntersectionObserver API

目前有一个新的 IntersectionObserver API,可以自动"观察"元素是否可见,Chrome 51+ 已经支持。

function query(selector) {
  return Array.from(document.querySelectorAll(selector));
}
var io = new IntersectionObserver(function(items) {
  items.forEach(function(item) {
   var target = item.target;
   if(target.getAttribute('src') == 'images/loading.gif') {
      target.src = target.getAttribute('data-src');
   }
  })
});
query('img').forEach(function(item) {
  io.observe(item);
});

demo3

IntersectionObserver 传入一个回调函数,当其观察到元素集合出现时候,则会执行该函数。 io.observe 即要观察的元素,要一个个添加才可以。 io 管理的是一个数组,当元素出现或消失的时候,数组添加或删除该元素,并且执行该回调函数。

除了在加载图片时可以使用懒加载,路由和组件也可以通过懒加载来减轻页面负担,可以看这篇Vue懒加载