前端实现图片的懒加载

1,298 阅读5分钟

前言 🛰🛰

我们在浏览网页的时候,比如像淘宝京东等一些大型的电商网站时,会发现会有很多的图片,如果一开始就展示所有的图片,势必会影响道性能,也对用户的体验不是很好,那么这个时候我通常就会使用图片的懒加载技术

什么是懒加载呢?🐤🐤

对于懒加载顾名思义就是当页面一开始渲染时不执行所有所有图片的渲染工作,而是当用户滚动页面时滚动到了这个图片,需要该图片加载时在加载图片,使用懒加载可以减少初次打开网站的时候的加载速度,提高网站的响应速度。

实现原理 🔑🔑

对于普通的网页渲染的时候,如果直接在src中引用url来展示图片,那么网页在渲染的时候就会下载该图片,而懒加载则是将图片存储在data-src中,一开始的时候并不直接渲染,当页面滚动到课时区域的时候,先通过getAttribute获取data-src中的url,然后通过setAttribute方法给img元素设置src,这样就完成了懒加载的核心思路。

实现方式 ⛹️‍♀️⛹️‍♀️

通过滚动监听+getBoundingClientRect实现

废话不多说,我们先实现一下核心的代码(JavaScript部分)

// 封装立即执行函数
((doc, win) => {
  //    获取图片数组
  const imgs = doc.querySelectorAll("img");
  function Toscroll() {
    imgs.forEach((item) => {
      //    获取图片到可视区域顶部的距离
      const imgRect = item.getBoundingClientRect();
      //   如果两者的差值是小于零的那么表示该图片在可视区域之内
      if (imgRect.top < window.innerHeight && imgRect.bottom > 0) {
        //  获取data-src中的url注意这里的写法,这是特有的
        let imgSrc = item.dataset.src;
        item.setAttribute("src", imgSrc);
      }
    });
  }
  Toscroll();
  window.addEventListener("scroll", Toscroll);
})(document, window);

这里因为代码很多都是重复的,只有图片不太一样,所以HTML部分不全部展示。

<div>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Molestiae praesentium asperiores recusandae,
 exercitationem autem a inventore! Vel eaque ullam provident, quas excepturi molestias beatae, rerum placeat
autem sequi inventore laudantium.
    ...
</p>
 <div class="img">
<img data-src="./image/图片1.jpg" src="./image/未加载在出来.jpg">
     ....
</div>       
</div>
​

讲解一下实现的核心思路:

首先大家肯定比较好奇getBoundingClientRect是什么,其实他返回是一个对象,表示了当前盒子在浏览器中的位置以及自身占据的空间的大小,除了width和height之外,其他的属性其实可以理解为相对于视窗左上角来计算出来的,下面我用一张图来给大家演示一下。

演示图片.jpg

那么有了这个属性有什么作用呢?我们可以根据获取到的top值来和window.innerHeight进行对比,如果top<innerHeight,并且bottom的值>0,则说明此时图片在可视区域内,那么此时就可以考虑显示真正的图片。

这里有一个小的技巧,我们在img元素上面设置的data-src,可以通过在遍历时通过item.dataset.src来拿到。然后通过setAttribute来设置src,当然可以通过getAttribute来获取。

但是我们还是要对细节进行一下优化,因为每当我们滑动滚轮的时候就会触发事件,这样事件触发的就太频繁了,所以我们就可以进行一下节流操作.这样我们就实现了图片的懒加载

((doc, win) => { 
    .....
//  节流函数
  function throttle(fn, time) {
    var begin = new Date().getTime();
    return function () {
      var _this = this;
      var cur = new Date().getTime();
      var args = arguments;
      if (cur - begin >= time) {
        fn.apply(_this, args);
        begin = cur;
      }
    };
  }
 window.addEventListener("scroll", throttle(Toscroll, 1000), false);
})(document, window);    

通过intersectionObserve来实现 🌶🌶

废话不多说,直接先上代码

((doc) => {
  let imgs = doc.querySelectorAll("img");
  //   console.log(imgs);
  function lazyLoading() {
    let observer = new IntersectionObserver((entires) => {
      console.log(entires);
      entires.forEach((item) => {
        // 获取到原来图片的元素
        let oImg = item.target;
        if (item.intersectionRatio > 0 && item.intersectionRatio <= 1) {
          oImg.setAttribute("src", oImg.dataset.src);
          observer.unobserve(item.target);
        }
      });
    });
    imgs.forEach((item) => {
      observer.observe(item);
    });
  }
  lazyLoading();
})(document);

首先介绍一下这个API:

  • intersectionObserve是浏览器原生提供的构造函数,接受两个参数:callback是可见性变化时的回调函数,option是配置对象(该参数可选),这个这个构造函数返回的是一个观察器实例,这个实例中需要调用observe方法传入一个DOM对象,可以用来指定观测那个DOM节点,当我们需要监测多个DOM节点的时候就需要多次调用observe方法。
  • 然后我们需要注意的是IntersectionObserver构造函数中的回调函数,当目标元素的可见性发生变化时(比如图片进入可见区或者离开可见区域)就会调用这个回调函数,同样这个回调函数接收一个参数,他是一个数组,其中的每一个元素都是IntersectionObserverEntry对象。
  • 这个对象中包含多个属性,比如代表原来监视的元素的target,我们可以通过这个获取DOM节点,还有就是代表所监视元素有多少进入了可视区域的intersectionRatio,可以用它来判断是否进入了可视区域。具体就是判断他的值是否在0到1之间。

预览效果

tutieshi_40x375_9s.gif

由此可以看出 不管是以上的那中方式都可以实现懒加载,大家可以根据个人的喜好来实现,当然使用API肯定是要相对的简单一些,

从右侧的网络请求状态我们可以看出,使用了图片懒加载之后,只有图片进入可视区域之后才会被加载。这样在一些图片特别多的网站中,这样就可以提升性能,避免在一开始就进行大量的图片渲染。

总结🥇🥇

通过对懒加载的学习,使我更加对前端的性能优化有了更多的认识,并且还认识到,知识是相互的,因为在这里就用到了之前封装过的节流函数,因此我们在学习的时候要多总结,这样才能稳步提高。