图片懒加载

101 阅读1分钟

1. 图片懒加载

图片懒加载,当图片没有进入我们的视觉范围内的时候不进行加载,这样可以解决一次性加载大量图片带来的性能问题。

需要解决的问题:

  • 如何判断在不在视口
  • 如何在出现时进行图片加载

2. 实现方式

2.1 设置img的loading

img元素设置loading属性,值为lazy

<img src="./example.jpg" loading="lazy" alt="lazyloading" height="200px" width="200px">

兼容性:

image.png

2.2 监听滚动 + 元素位置判断

image.png

2.2.1 位置判断

使用getBoundingClientRect()

  • img.getBoundingClientRect().top:图片距离视口顶部的距离
  • window.innerHeightdocument.documentElement.clientHeight:视口高度

image.png

如果 图片距离视口顶部的距离 < 视口高度,说明图片出现在视口中了

image.png

2.2.2 图片加载

<img data-src='xxx.jpg' />

使用data-*自定义属性,浏览器不会进行属性处理

js部分

const images = document.querySelectorAll('img')

window.addEventListener('scroll', (e)=>{
    images.foreach(image =>{
        const imageTop = image.getBoundingClientRect().top
        if(imageTop < window.innerHeight){
            const data_src = image.getAttribute('data-src')
            image.setAttribute('src',data-src)
        }
    })
})

缺点: 滚动事件被频繁触发,加载后还是会被触发

可以增加防抖,debounce

2.2 使用 IntersectionObserver

IntersectionObserver 接口提供了一种异步观察目标元素与其祖先元素或顶级文档视口(viewport)交叉状态的方法。其祖先元素或视口被称为根(root)。

当一个 IntersectionObserver 对象被创建时,其被配置为监听根中一段给定比例的可见区域。一旦 IntersectionObserver 被创建,则无法更改其配置,所以一个给定的观察者对象只能用来监听可见区域的特定变化值;然而,你可以在同一个观察者对象中配置监听多个目标元素

image.png

创建一个观察者实例,构造函数接受一个callback,会在交叉状态更改的时候调用。观察者使用observer来对DOM节点进行观察,使用unobserve来取消观察。

HTML部分同上。

JS部分重点:

  • 回调函数所接受参数为 子元素为对象的数组
  • 使用对象的 isIntersecting 来判断交叉状态
  • 使用对象的 target 来获取触发的元素

JS部分

const images = document.querySelectorAll('img')
// 或者
// const images = document.querySelectorAll('[data-src]')

const callback = (entries) => {
    entries.forEach(entry => {
        if(entry.isIntersecting){ // 根据entry对象的 isIntersecting 来判断是否交叉
            const image = entry.target; // 根据entry对象的taget来获取目标图片节点
            const data_src = image.getAttribute('data-src')
            image.setAttribute('src', data_src);
            observer.unobserve(image); // 停止观察已处理的元素
        }
    })
}

const observer = new IntersectionObserver(callback)

images.forEach(image => { // 观察者对所有图片进行观察
    observer.observe(image)
})