实现图片懒加载的最佳解决方法IntersectionObserver

213 阅读2分钟

为什么要图片懒加载?

进入某个页面时,会有很多图片,有些图片是在下面的,当我们没有滑动到下面时,那些图片还不需要加载的。如果我们加载了也是白加载,这样只会降低网页的加载速度。所以我们需要懒加载,只有滑动到可视区域才会加载当前需要的图片。

实现原理

图片的加载是由src的值引起的,当对src赋值时浏览器就会请求图片资源。我们可以使用html5的自定义属性data-xxx(data-src)来保存真实的路径,当需要加载时(到达可视区域),才将data-xxx的值赋给src

1. 图片还没在可视区域时(data-xxx保存真实路径)

image.png

2. 图片在可视区域时(将data-xxx的值赋予src)

image.png

如何判断是否在可视区域

图片距离视窗顶部的距离(getBoundingClientRect().top)小于窗口显示区的高度(window.innerHeight),就在可视区域,如下图蓝色图片。

image.png

具体实现代码


    <div>你好你好</div>

    <div>你好你好</div>

    <div>你好你好</div>

    <div>你好你好</div>

    <div>你好你好</div>

    <div>你好你好</div>

    <img style="margin-left: 30%;margin-top: 50px;margin-bottom: 50px;"

        data-src="1.webp" alt="">

    <img style="margin-left: 30%;margin-top: 50px;margin-bottom: 50px;"

        data-src="2.webp" alt="">

    <img style="margin-left: 30%;margin-top: 50px;margin-bottom: 50px;"

        data-src="3.webp"

        alt="">

    <div>你好你好</div>

    <div>你好你好</div>

    <div>你好你好</div>

    <div>你好你好</div>

</body>

<style>

    div {

        height: 350px;

        font-size: 50px;

        margin: 0 30%;

    }

</style>

Script部分

事件监听scroll滚动事件,获取每张图片距离视窗顶部的距离(设置为a)和窗口显示区的高度(设置为b)

如果a<b, 就在可视区域内,就将data_src的值赋予src

这样就可以实现图片懒加载。


    // 原始方法

    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);

                console.log('scroll触发了')

            }

        });

})

</script>

上面的代码,滚动事件会被触发很多次,而且图片已经加载了还是会触发滚动事件,非常消耗资源

image.png

第二种解决方法:

因此,目前最推荐使用的还是 IntersectionObserver

IntersectionObserver是浏览器提供的构造函数(有些浏览器不支持)

IntersectionObserver实例有两个方法,observe(观察一个节点)和unobserve(解除观察)

实例传入一个参数,是回调函数,目标元素(图片)看见了触发一次,看不见也触发一次

image.png 这个回调函数接收一个数组参数,数组的每一项都有isIntersecting属性,代表是否在可视区域;而target代表着目标元素

所以当isIntersecting为true时,就将这个元素的data_src的值赋予src

然后再解除对这张图片的观察,事件只会被触发3次

image.png

代码如下:


 

// 使用IntersectionObserver构造函数

    // IntersectionObserver实例有两个方法,observe(观察一个节点)和unobserve(解除观察)

    const images = document.querySelectorAll('img');

    const callback = (entries) => {

        entries.forEach(entry => {

            // 每个entry数组都有一个isIntersecting属性(是否在可视区域)

            if (entry.isIntersecting) {

                // target代表着目标元素

                const image = entry.target;

                const data_src = image.getAttribute('data-src');

                image.setAttribute('src', data_src);

                observer.unobserve(image);

                console.log('触发了')

            }

        })

    }

    const observer = new IntersectionObserver(callback);

    images.forEach(image => {

        observer.observe(image);

    })

</script>