开发中实现图片懒加载

图片懒加载,即在图片出现在视口内或即将出现在视口内时再加载图片。图片懒加载可以分解为两个问题:

  • 如何判断图片在视口内
  • 如何控制加载图片

计算图片位置 + 滚动事件 + DataSet API

先设置一个临时的data属性的src占位

<img data-src="./images/01.jfif">

然后就可以下面的isShow()函数判断该图片是否在视口中,即元素相对顶点的值<=滚动的距离+窗口的高度 [ + 预留的加载距离]

function isShow($node, preDistance) {
    return $node.offset().top <= $(window).height() + $(window).scrollTop() + preDistance;
}

当图片出现(或即将出现)在视口,就可以控制图片开始加载,这里使用datasetAPI

function lazyload($imgs) {
    $imgs.each(function(idx, img) {
        if (img.src) return;
        if (isShow($(img), 100)) {
            img.src = img.dataset.src;
        }
    })
}

在页面加载的同时就要先调用一次lazyload()函数,让已经在视口中(首屏中)的图片开始加载,然后监听scroll事件,并在事件监听器中调用lazyload()函数。

lazyload($imgs);

window.onscroll = function (evt) {
    lazyload($imgs);
}

getBoundingClientRect + 防抖 + DataSet API

这里引入Element.getBoundingClientRect()方法,此方法返回一个DOMRect对象,这个对象中提供了元素大小和相对于视口(左上角)的位置信息

如此,就可以不通过jQuery来获取元素的位置了。

function isShow(img, preDistance) {
    return img.getBoundingClientRect().top <= document.documentElement.clientHeight + preDistance;
}

同样的,在lazyload()函数中用for循环代替jQuery元素的each()方法。

function lazyload(imgs) {
    for (let i = 0, len = imgs.length; i < len; i++) {
        const img = imgs[i];
        if (img.src) continue;
        if (isShow(img, 100)) {
            img.src = img.dataset.src;
        }
    }
}

由于滚动事件是高频事件,加个节流提高性能。一般使用lodash就够了。

// 获取元素
const imgs = document.querySelectorAll('img');
// 首屏加载
lazyload(imgs);

function handleScroll() {
    lazyload(imgs);
}

window.onscroll = _.throttle(handleScroll, 136);

IntersectionObserver API + DataSet API

以上两种方法都是固定套路:计算位置,加载图片,节流。于是浏览器新增了一个三合一的API:IntersectionObserver(IE不支持)

  • IntersectionObserver是一个类,实例化时传入一个回调函数作为构造函数参数。
  • 这个回调函数接收一个changes参数,是一个对象数组,包含一系列变化对象。
  • changes数组中的某一个对象的isIntersecting属性为true时,表示被观察对象出现在视口中。这时可以写入图片加载程序,这里同样使用DataSet API
  • 图片加载程序完成后,使用observer.unobserve()方法取消订阅change.target对象,以防止重复加载。
const observer = new IntersectionObserver(changes => {
    changes.forEach(change => {
        if (change.isIntersecting) {
            const item = change.target;
            item.src = item.dataset.src;
            observer.unobserve(item);
        }
    })
});

此对象实例化后,就可以使用observer.observe()方法订阅所有需要懒加载的图片

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

imgs.forEach(img => {
    observer.observe(img);
})

IntersectionObserver 除了给图片做懒加载外,还可以对单页应用资源做预加载。

LazyLoading属性

这应该是最简单的方式了,不过对前端而言,越简单代表兼容性越不好。
同时,使用loading="lazy"后,并不是图片刚好进入视口时才加载,而是预留了一部分的加载距离。

<img src="./images/01.jfif" loading="lazy">

[参考资料] https://q.shanyue.tech/fe/html/1.html

原文地址:https://www.cnblogs.com/hycstar/p/14621268.html