一篇学会如何实现图片懒加载,优化性能

361 阅读7分钟

引言

不知道大家在网站上浏览的时候有没有发现过这样一个问题:图片是逐行加载出来的,而不像我们写平常的html界面,图片是全部一起加载完成的。这就涉及到图片懒加载的知识了,图片懒加载可以显著减少性能的损耗,是我们开发过程中必备的技能。今天我们就来了解如何实现这个功能。


图片懒加载说白了就是图片延迟加载,由于浏览器的可视范围是有限的,而现在网页的内容越来越丰富,浏览内容需要进行滚动才能完成。当一个网页上有很多图片,而我们知道图片又是非常耗流量的,如果用户还没看到网页下的内容,那我们就没必要一下子加载这些看不见的图片。

监听事件

既然要滚动到网页下面才能预览到看不见的图片,那我们可以采用事件监听scroll这个事件进行解决。监听scroll事件,鼠标滚动就会触发。

这里我们需要知道两个高度

  • 窗口显示区的高度(window.innerHeight)
  • 图片到视窗上边的距离(可用getBoundingClientRect().top获取)

如果还不能看到图片,即图片距离视窗顶部的距离大于等于窗口显示区的高度

如果可以看到图片,即图片距离视窗顶部的距离小于窗口显示区的高度 image.png

<body>
  <p>我是一段文字,我是凑字数用的,接下来就是见证奇迹的时刻,今年过节不收礼,收礼只收脑白金,喜之郎,多点关心多点爱,海的味道我知道,波力海苔点心面</p>
  <p>我是一段文字,我是凑字数用的,接下来就是见证奇迹的时刻,今年过节不收礼,收礼只收脑白金,喜之郎,多点关心多点爱,海的味道我知道,波力海苔点心面</p>
  <p>我是一段文字,我是凑字数用的,接下来就是见证奇迹的时刻,今年过节不收礼,收礼只收脑白金,喜之郎,多点关心多点爱,海的味道我知道,波力海苔点心面</p>
  <p>我是一段文字,我是凑字数用的,接下来就是见证奇迹的时刻,今年过节不收礼,收礼只收脑白金,喜之郎,多点关心多点爱,海的味道我知道,波力海苔点心面</p>
  <p>我是一段文字,我是凑字数用的,接下来就是见证奇迹的时刻,今年过节不收礼,收礼只收脑白金,喜之郎,多点关心多点爱,海的味道我知道,波力海苔点心面</p>
  <img data-src="1.gif">
  <img data-src="2.gif">
  <img data-src="3.gif">
  <p>我是一段文字,我是凑字数用的,接下来就是见证奇迹的时刻,今年过节不收礼,收礼只收脑白金,喜之郎,多点关心多点爱,海的味道我知道,波力海苔点心面</p>
  <p>我是一段文字,我是凑字数用的,接下来就是见证奇迹的时刻,今年过节不收礼,收礼只收脑白金,喜之郎,多点关心多点爱,海的味道我知道,波力海苔点心面</p>
  <p>我是一段文字,我是凑字数用的,接下来就是见证奇迹的时刻,今年过节不收礼,收礼只收脑白金,喜之郎,多点关心多点爱,海的味道我知道,波力海苔点心面</p>
<script src="index.js">
  
</script>
</body>
</html>

使用自定义属性 data-,浏览器不会像默认属性那样处理这个自定义的属性。

const images = document.querySelectorAll('img')//获取所有img标签

window.addEventListener('scroll',(e)=>{//判断每张图片的位置是否出现在可视区域
  images.forEach(image=>{
    const imageTop = image.getBoundingClientRect().top//每次遍历的时候,获取每张图片到顶部的距离
    //判断
    if(imageTop<window.innerHeight){//图片距视图顶部距离小于窗口显示区高度
      //获取刚刚取得的自定义属性
      const data_src = image.getAttribute('data-src')
      //将自定义属性赋值给原本的src属性
      image.setAttribute('src',data_src)
    }
    console.log('scroll触发');
  })
})

我们看一下这个结果:

动画27.gif

虽然确实实现了懒加载,但是我们看到控制台中srcoll事件被触发了很多次,页面需要加载很多内容就会导致任务堆积。而且即使图片加载了还是会不断触发事件,非常消耗资源,所以我们更推荐IntersectionObserver这种方法。

IntersectionObserver

IntersectionObserver是浏览器提供的构造函数(但是部分浏览器版本不兼容)

IntersectionObserver(交叉观察),目标元素和可视窗口会产生交叉区域

使用方法

因为是构造函数,首先我们需要new一个用于观察的实例

const observer = new IntersectionObserver

进行观察

observer.observe(DOM节点)

当一些图片加载出来之后就没有必要观察了,所以还需要一个方法结束观察

observer.unobserve(DOM节点)

我们在观察到目标DOM节点的时候需要进行相应的动作,需要函数来进行封装这些动作,所以IntersectionObserver 的构造函数需要接受两个参数:

  1. 回调函数:当被观察的元素的可见度发生变化时,这个回调函数会被调用。
  2. 选项对象:一个可选的参数,用于指定如何观察元素。
const observer = new IntersectionObserver(callback, options);
参数详解
  • callback

    • 类型:Function

    • 作用:当被观察元素的可见性发生变化时,这个回调函数会被调用,一般触发两次。

    • 回调函数接收两个参数:

      • entries:一个数组,包含所有发生可见度变化的元素。
      • observerIntersectionObserver 实例本身。
  • options

    • 类型:Object

    • 作用:用于配置观察行为。

    • 可配置的属性包括:

      • root:一个元素节点,作为观察的相对容器。默认为 null,表示相对于视口。
      • rootMargin:一个字符串,定义了相对于 root 的边界框边缘的偏移量。默认为 '0px'
      • threshold:一个数字或数字数组,定义了在什么比例的可见度时触发回调。默认为 [0],表示只要元素可见就触发回调。

虽然我们知道了回调函数的触发条件,但是我们并不知道触发回调函数时图片处于什么位置,所以这里的回调函数需要接受一个参数,这个参数是一个数组,表示为 entries 在控制台输出的话可以看到这些:

image.png

使用foreEach遍历这个数组,并输出每一次触发的细节

const callback =(entries)=>{
  entries.forEach(entry=>{
    console.log(entry);
  })
};                                                                                                                                            

运行我们的html页面,打开控制台,着重看isIntersecting属性,目前还没加载到图片,都为false:

image.png 然后我们滚动页面至图片到达可视区域,isIntersecting属性更新为true:

image.png

isIntersecting属性可以帮助我们判断该次触发回调函数时是否已经观察到了图片,但是控制台中显示触发了3次回调函数,所以我们还要通过target属性确定对应触发isIntersecting更改为true的元素。

target属性指的就是目标元素。

const callback =(entries)=>{
  entries.forEach(entry=>{
    if(entry.isIntersecting){//如果观察到了图片
      const image = entry.target;
      const data_src = image.getAttribute('data-src')//获取相应图片节点
      image.setAttribute('src',data_src)//修改为常规的src属性
      console.log('触发');
    }
  })
};

我们查看控制台,当我们上下来回滚动的时候,回调函数也会重复执行:

动画28.gif

所以我们要在图片被加载后取消回调函数的触发

取消回填函数的触发并不是取消滚动,而是直接取消observer实例这个观察动作

const callback =(entries)=>{
  entries.forEach(entry=>{
    if(entry.isIntersecting){//如果观察到了图片
      const image = entry.target;
      const data_src = image.getAttribute('data-src')//获取相应图片节点
      image.setAttribute('src',data_src)//修改为常规的src属性
      observer.unobserve(image)//取消观察
      console.log('触发');
    }
  })
};

动画29.gif

这样就算来回滚动窗口也不会重复执行啦。


结语

以上就是关于图片懒加载的介绍,本篇文章是参考了技术蛋老师的视频原地址:www.bilibili.com/video/BV1FU…

蛋老师讲的非常详细,感兴趣的小伙伴强烈推荐观看原视频。 最后,希望本文对你有所帮助,感谢你的阅读!