图片懒加载

140 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情

针对于性能要求较高的网站,或者图片信息过多的网站,加载首屏时,会发送大量的图片请求,而大多图片不在视口中,也会被请求,不但增加了服务器压力,也占用了浏览器的大部分性能,造成首屏显示慢和加载卡顿等问题,这时就需要我们把请求中的不在视口内的图片过滤掉,初次不发送请求,只有当用户往下翻页时,再像服务器发请求,这个就图片懒加载技术。目的就是性能优化,提升用户体验,

方法一

监听window 的 scroll事件 根据这个效果,方法一 首先这可能使用到一点小技巧,视口外的img可以不给src属性,或者给一个小的loading图片(体积很小的,引起loading相同,多个图片也只发一次请求),然后把真的src,保存在data-src上, 监听window.onscroll事件, 当发生scroll事件时,遍历一下所有未请求的图片,判断一下具体顶部的位置(element.getBoundingClientRect().top;),如果图片距离顶部的位置小于等于视口(window.innerHeight)的高度,那么图片就要显示了,就要发送请求(当前对用户体验要求较高的话,可以根据情况,比如图片距离顶部的高度小于(视口高度加上500px)时就加载图片), 加载图片方法很简单,就是把正确的src赋值给img的src属性,浏览器就会自动发送请求了

  <body>
      /// 很多很多其他dom,下面是不会在首屏的
    <img alt="" data-src="./1.gif" />
    <img alt="" data-src="./2.gif" />
    <img alt="" data-src="./3.gif" />
    <img alt="" data-src="./4.gif" />
  </body>
  <script>
    var imgs = document.querySelectorAll("img");
    window.addEventListener("scroll", (e) => {
      imgs.forEach((img) => {
        const imgTop = img.getBoundingClientRect().top;
          // 这里可随意提前一些加载,不要图片漏出了才加载,因为需要一点提前量
        if (imgTop < window.innerHeight && !img.src) {
          const src = img.dataset.src;
          img.src = src;
        }
      });
    });
  </script>

这中方法简单易用,但是缺点就是onscroll事件触发太频繁,很浪费性能。

方法二

intersectionObserve api,这个是我们项目里在用的,因为是原生支持的,所以性能比较好, 缺点,可能是兼容性不太好吧,毕竟曾经微软比较牛逼

image.png

 IntersectionObserver API,可以自动"观察"元素是否可见,Chrome 51+ 已经支持。由于可见(visible)的本质是,目标元素与视口产生一个交叉区,所以这个 API 叫做"交叉观察器"。 这个api是真的好用,有一些业务会dom级别的view事件的埋点,这个方法也是不二的选择,具体api使用,自己看官网,接口简单,功能复杂,是个好东西

   <script>
   let imgs = document.getElementsByTagName("img");
   // 1. 一上来立即执行一次
   let io = new IntersectionObserver(function (entires) {
     //图片进入视口时就执行回调
     entires.forEach((item) => {
       // 获取目标元素
       let oImg = item.target;
       console.log(item.intersectionRatio);
       // 当图片进入视口的时候,就赋值图片的真实地址
       if (item.intersectionRatio > 0) {
         oImg.src = oImg.dataset.src;
         // src 赋值完了以后,就不用观察这dom是否显示在视口了
         io.unobserve(oImg);
       }
     });
   },
     {
     // rootMargin是给img增加了margin top right bottom left 各增加了200px 也就是适口距离真实dom200px时,就代表dom已经显示在视口了,intersectionRatio就大于0了,就加载图片了,搞点提前量
       rootMargin: "200px",
     });
   Array.from(imgs).forEach((element) => {
     io.observe(element); //给每一个图片设置监听
   });
 </script>