让你更优雅地实现图片懒加载的两种方法

前言

在之前的文章十分钟搞懂前端高频考点:图片懒加载中我提到了如何手搓一个图片懒加载,当时还自以为是地优化了一下,觉得这样已经足够优雅了,直到看到了另外两位大佬的指点,我才发现还有许多不足之处。既然有大佬指点,那当然是再好不过,按捺住鸡冻的心,颤抖的手,我立马加深了对图片懒加载的学习,今天继续往下聊聊,另外两种能够更优雅地实现图片懒加载的方法。

正文

在之前的文章中已经提到了什么是图片懒加载,在这里就不过多赘述了。今天介绍的两种方法分别是loading和IntersectionObserver

Loading

作为一个HTML5中新增加的属性,默认情况下的值为eager,但将img标签的loading属性设置为lazy之后,浏览器会自动将图片视为懒加载图片,也就是当浏览器进入视窗时才会加载,而未进入视窗的图片则不加载。loading MDN文档

html 复制代码
<img src="fakelocation" loading="lazy" alt="">

这一属性大大降低的图片懒加载的复杂度,使开发者不再需要编写复杂的js代码,同时,也避免了过度监听滚动事件从而造成大量的回调函数执行,优化了性能。

什么是IntersectionObserver

聊完loading再来聊聊IntersectionObserver。loading已经可以说是无可挑剔了,但依旧存在浏览器不支持的情况。那么这个时候,我们可以用IntersectionObserver去更优雅地实现图片懒加载。这个方法虽然更复杂一些,甚至比手搓一个都复杂,但是不得不承认在性能优化上要比原生js来的更好。

IntersectionObserver 是一种浏览器提供的API,用于异步观察目标元素与视口(viewport)或其他元素的交叉状态变化。这个API特别适用于提升网页性能和实现特定的交互效果,也就是说除了懒加载图片,还可以实现无限滚动或者在元素进入可视区域时启动动画等。

IntersectionObserver的使用

IntersectionObserver 是一个构造函数,使用前我们需要将其实例化,在实例化的过程中,添加一个回调函数作为参数,当观察的目标元素进入或退出视口,或者与设定的根元素发生交叉时,该回调函数会被触发。这个过程是异步的,并且只有在浏览器空闲时才会执行,这也就是为什么相比使用scroll事件监听等方法,它能更高效地利用资源,减少页面重绘和回流,从而提升用户体验和页面性能。

js 复制代码
document.addEventListener('DOMContentLoaded', function () {
    // 创建IntersectionObserver实例
    const observer = new IntersectionObserver((entries, observer) => {
        entries.forEach(entry => {
            // 如果元素与视口相交
            if (entry.isIntersecting) {
                // 获取当前图片元素的data-src属性并设置为src属性,实现图片加载
                const img = entry.target;
                const src = img.getAttribute('data-src');

                // 设置src属性,触发图片加载
                img.src = src;

                // 加载完成后,可以停止观察此元素
                observer.unobserve(img);
            }
        });
    });
    // 选择所有需要懒加载的图片元素并开始观察它们
    const lazyImages = document.querySelectorAll('.lazy-image');
    lazyImages.forEach(img => {
        observer.observe(img);
    });
});

作为参数的回调函数接受两个参数,第一个是entries,代表所有被观测的元素,是一个数组。而第二个observer则是观察器自身,通常情况下并不需要使用所以也就不做过多介绍了(其实就是我太菜学不明白,只知道可以修改观察器的配置,解除对某些元素的观察。欢迎各位读者大佬指点。)

值得一提的是,IntersectionObserver构造函数还可以接受第二个参数作为其配置项,控制图片开始加载的时刻,参数的数据类型是对象,其中有两个键值对分别是rootMargin和threshold,用法如下

js 复制代码
rootMargin: '0px',// 配置选项,比如阈值,默认为0,即只要元素有一部分可见就触发
threshold: 0.1 // 可以设置多个阈值,这里表示元素至少10%可见时就触发

如此一来,通过IntersectionObserver就可以实现图片懒加载了。

总结

通常情况下,我个人肯定更倾向于使用loading标签,但是由于兼容性问题,我们需要在开发过程中多做一些准备,确保图片懒加载能够如期进行,以下是完整的代码

js 复制代码
// 检查浏览器是否支持原生的 lazy loading
function supportsLazyLoading() {
    const img = new Image();
    return 'loading' in img;
}

// 如果浏览器不支持原生lazy loading,则使用JavaScript实现懒加载
function enableLazyLoadingFallback() {
    let images = document.querySelectorAll('img[data-src][loading="lazy"]');
    let viewHeight = window.innerHeight;
    let intersectionObserverSupported = ('IntersectionObserver' in window);

    function loadImage(img) {
        img.src = img.dataset.src;
        img.removeAttribute('loading');
        img.removeAttribute('data-src');
    }

    if (intersectionObserverSupported) {
        const observer = new IntersectionObserver((entries, observer) => {
            entries.forEach(entry => {
                if (entry.isIntersecting) {
                    loadImage(entry.target);
                    observer.unobserve(entry.target);
                }
            });
        });

        images.forEach(img => {
            observer.observe(img);
        });
    } else {
        // 对于不支持IntersectionObserver的浏览器,可以考虑使用scroll事件的简单监听
        // 注意:这种方式效率较低,可能引起性能问题
        window.addEventListener('scroll', () => {
            images.forEach(img => {
                if (img.getBoundingClientRect().top < viewHeight + window.scrollY) {
                    loadImage(img);
                }
            });
        });
    }
}
if (!supportsLazyLoading()) {
    enableLazyLoadingFallback();
}

作为一个小白,我自知掌握的深度有限,因此也恳请各位读者姥爷能够多加指点。最后祝各位0 waring(s),0 error(s)。

相关推荐
安冬的码畜日常4 分钟前
【CSS in Depth 2 精译_036】5.6 Grid 网格布局中与对齐相关的属性 + 5.7本章小结
前端·css·css3·html5·网格布局·grid·css网格
啧不应该啊1 小时前
vue配置axios
前端·javascript·vue.js
__fuys__2 小时前
【HTML样式】加载动画专题 每周更新
前端·javascript·html
Want5952 小时前
HTML粉色烟花秀
前端·css·html
让开,我要吃人了2 小时前
HarmonyOS鸿蒙开发实战(5.0)自定义全局弹窗实践
前端·华为·移动开发·harmonyos·鸿蒙·鸿蒙系统·鸿蒙开发
yanlele2 小时前
前端面试第 66 期 - Vue 专题第二篇 - 2024.09.22 更新前端面试问题总结(20道题)
前端·javascript·面试
一条晒干的咸魚2 小时前
响应式CSS 媒体查询——WEB开发系列39
前端·css·html·css3·响应式设计·媒体查询
凌晨五点的星2 小时前
网络安全-webshell绕过,hash碰撞,webshell绕过原理
开发语言·前端·javascript
天心天地生3 小时前
【bugfix】-洽谈回填的图片消息无法显示
开发语言·前端·javascript
啧不应该啊3 小时前
element plus 按需导入vue
前端·javascript·vue.js