乐趣区

关于javascript:图片懒加载

为什么要懒加载?

懒加载是一种网页性能优化的形式,它能极大的晋升用户体验。

如果拜访某个页面,加载这个页面全副的图片(即便这些图片并不处在用户的以后的视窗中),在弱网环境或者网速较慢的环境下,这些“冗余”图片的下载会占用用户原本就十分无限的带宽,影响用户体验所以对于网站的图片,优化的办法就是懒加载(按需加载)。

实现原理

进入页面的时候,只申请可视区域的图片资源,不在可视区域内的图片,等将要呈现时动静加载。

将 img 标签 src 设置为一张默认小图片,而后定义 data-src(这个属性能够自定义命名,我才用 data-src)属性指向实在的图片。src 需设置一张默认的图片,否则当 src 为空时也会向服务器发送一次申请。

一个简略的实现

设置 data-src,通过 js 加载真正的图片。

// img-load.css
img[data-src] {filter: blur(0.2em);
}

img {filter: blur(0em);
  transition: filter 0.5s;
}
// img-load.js

function lazyLoad(){const imageToLazy = document.querySelectorAll('img[data-src]');
  const loadImage = function (image) {image.setAttribute('src', image.getAttribute('data-src'));
    image.addEventListener('load', function() {image.removeAttribute("data-src");
    })
  }

  imageToLazy.forEach(function(image){loadImage(image);
  })
}

lazyLoad();

return (
  ...
    ![](deafult.png)
  ...
);

监听滚动加载图片

先加载呈现在视窗中的图片,当页面将要滚动到不在视窗内的图片时,加载图片。

  1. 用 IntersectionObserver 实现当图片滚动到视窗后再加载该图片,思考到手机兼容性问题须要增加 polyfill
(function lazyLoad(){
  let observer;
  const imageToLazy = document.querySelectorAll('img[data-src]');
  
  const loadImage = function (image) {image.setAttribute('src', image.getAttribute('data-src'));
    image.addEventListener('load', function() {image.removeAttribute("data-src");
    })
  }


  if (window.IntersectionObserver) {observer = new IntersectionObserver((entries) => {
      entries.forEach(entry => {if (entry.isIntersecting || entry.intersectionRatio > 0) {loadImage(entry.target);
          observer.unobserve(entry.target);
        }
      });
    });
  }

  imageToLazy.forEach(function(image){if (window.IntersectionObserver && observer) {observer.observe(image);
    } else {loadImage(image);
    }
  })
})();
  1. 增加 scroll 事件实现图片懒加载。当页面滚动时,scroll函数会被高频触发,须要通过节流或者 requestAnimationFrame 优化性能。

    判断图片是否将要呈现在可视区也可依据 getBoundingClientRect() 判断

    const imgs = document.getElementsByTagName("img");
    const imgLength = imgs.length;
    let currentIndex = 0; // 存储图片加载到的地位,防止每次都从第一张图片开始遍历
    
    const clientHeight = document.documentElement.clientHeight; // 可见区域高度
    const scrollTop = document.documentElement.scrollTop || document.body.scrollTop; // 滚动条间隔顶部高度
    
    function lazyload(event) {for (let i = currentIndex; i < imgLength; i++) {if (clientHeight + scrollTop > imgs[i].offsetTop) {if (imgs[i].getAttribute("src") == "default.jpg") {imgs[i].src = img[i].getAttribute("data-src");
          }
          currentIndex = i + 1;
        }
      }
    }
    window.addEventListener('scroll',throttle(lazyload, 500, 1000));
  2. vue 指令实现图片懒加载
let timer = null;
let observer;

if (window.IntersectionObserver) {observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {if (entry.isIntersecting || entry.intersectionRatio > 0) {if (!entry.target.isLoaded) {showImage(entry.target, entry.target.data_src);
        }
      }
    });
  });
}

function showImage(el, imgSrc) {const img = new Image();
  img.src = imgSrc;
  img.onload = () => {
    el.src = imgSrc;
    el.isLoaded = true;
  };
}

export default {inserted(el, binding, vnode) {
    el.data_src = binding.value;
    if (window.IntersectionObserver && observer) {clearTimeout(timer);
      observer.observe(el);
      const vm = vnode.context;
      timer = setTimeout(() => {vm.$on('hook:beforeDestroy', () => {observer.disconnect();
        });
      }, 20);
    } else {el.src = binding.value;}
  },
  update(el, binding) {if (window.IntersectionObserver && observer) {
      el.isLoaded = el.data_src !== binding.value;
      el.data_src = binding.value;
    } else {el.src = binding.value;}
  },
}
退出移动版