懒加载

114 阅读2分钟

在项目中使用常规的懒加载方式处理些资源,会在一些地方都需要添加懒加载处理(比如页面进入时候,滚动时候,部分资源异步加载等),同一个页面内重复触发懒加载,对于性能也会存在一部分的影响;还有部分手机机型有可能屏幕高度比较小,没必要一开始进入就懒加载,因为这个原因,寻找一个比较合理合适的解决方案处理。

1、常规懒加载

使用图片资源懒加载作为例子,一般都是在页面中进行资源监测,进入页面触发一次懒加载,滚动时候再绑定事件触发懒加载,这样页面就一开始进行了两次处理。如果页面有横向滚动的元素,就需要再一次绑定懒加载处理,有多个这种横向滚动等场景,就需要多次绑定处理。

element-box-diagram.png

    function   throttle(fn, wait) {
        let inThrottle, lastFn, lastTime;
        return function () {
          const context = this,
            args = arguments;
          if (!inThrottle) {
            fn.apply(context, args);
            lastTime = Date.now();
            inThrottle = true;
          } else {
            clearTimeout(lastFn);
            lastFn = setTimeout(function () {
              if (Date.now() - lastTime >= wait) {
                fn.apply(context, args);
                lastTime = Date.now();
              }
            }, Math.max(wait - (Date.now() - lastTime), 0));
          }
        };
      }
       const lazyLoadImgObj = {
          isIn(el) {
            var bound = el.getBoundingClientRect();
            var clientHeight = window.innerHeight;
            return el.getAttribute("lazy") === "loading" && bound.top <= clientHeight;
          },
          checkImg() {
            let imgs = document.querySelectorAll("img[lazy='loading']");
            Array.from(imgs).forEach((el) => {
              if (lazyLoadImgObj.isIn(el)) {
                lazyLoadImgObj.loadImg(el);
              }
            });
          },
          loadImg(el) {
            if (el.src !== el.dataset.src) {
              el.src = el.dataset.src;
              el.setAttribute("lazy", "loaded");
            }
          },
        };
        const checkImgFunc = throttle(lazyLoadImgObj.checkImg, 500);
        document.addEventListener('DOMContentLoaded', checkImgFunc);
        window.addEventListener('scroll', checkImgFunc);
<body style="display: flex;flex-direction: column;">
        <img lazy='loading' src="https://www.minigame.vip/static/images/lazy-img.png" data-src="https://res.minigame.vip/gc-assets/bubble-shooter/bubble-shooter_banner.webp" style="height: 300px;width: 600px;">
        <img lazy='loading' src="https://www.minigame.vip/static/images/lazy-img.png" data-src="https://res.minigame.vip/gc-assets/bubble-shooter/bubble-shooter_banner.webp" style="height: 300px;width: 600px;">
        <img lazy='loading' src="https://www.minigame.vip/static/images/lazy-img.png" data-src="https://res.minigame.vip/gc-assets/bubble-shooter/bubble-shooter_banner.webp" style="height: 300px;width: 600px;">
        <img lazy='loading' src="https://www.minigame.vip/static/images/lazy-img.png" data-src="https://res.minigame.vip/gc-assets/bubble-shooter/bubble-shooter_banner.webp" style="height: 300px;width: 600px;">
        <img lazy='loading' src="https://www.minigame.vip/static/images/lazy-img.png" data-src="https://res.minigame.vip/gc-assets/bubble-shooter/bubble-shooter_banner.webp" style="height: 300px;width: 600px;">
        <img lazy='loading' src="https://www.minigame.vip/static/images/lazy-img.png" data-src="https://res.minigame.vip/gc-assets/bubble-shooter/bubble-shooter_banner.webp" style="height: 300px;width: 600px;">
        <img lazy='loading' src="https://www.minigame.vip/static/images/lazy-img.png" data-src="https://res.minigame.vip/gc-assets/bubble-shooter/bubble-shooter_banner.webp" style="height: 300px;width: 600px;">
        <img lazy='loading' src="https://www.minigame.vip/static/images/lazy-img.png" data-src="https://res.minigame.vip/gc-assets/bubble-shooter/bubble-shooter_banner.webp" style="height: 300px;width: 600px;">
        <img lazy='loading' src="https://www.minigame.vip/static/images/lazy-img.png" data-src="https://res.minigame.vip/gc-assets/bubble-shooter/bubble-shooter_banner.webp" style="height: 300px;width: 600px;">
        <img lazy='loading' src="https://www.minigame.vip/static/images/lazy-img.png" data-src="https://res.minigame.vip/gc-assets/bubble-shooter/bubble-shooter_banner.webp" style="height: 300px;width: 600px;">
</body>

2、使用IntersectionObserver处理

  • 含义:web api 提供了一种异步观察目标元素与其祖先元素或顶级文档视口(viewport)交叉状态的方法 主要是用来监听可见区域的变化
  • root参数配置:表示需要与元素交互的跟元素,默认null表示视口
  • rootMargin参数配置:表示根元素需要外拓的范围

image.png

  • thresholds参数配置:表示元素进入监听区域内的所占比列,默认为0,表示两个刚好碰到,才会触发回调。为1,表示完全进入,才触发回调

image.png

image.png

  • 兼容性:

image.png

        <script>
        // 懒加载函数
        function lazyLoadImages() {
            // 选择所有需要懒加载的图片元素
            const images = document.querySelectorAll("img[lazy='loading']");

            // 检查图片是否在视口内的函数
            const isInViewport = (image) => {
                const rect = image.getBoundingClientRect();
                return (
                    rect.top <= window.innerHeight &&
                    rect.left <= window.innerWidth &&
                    rect.bottom >= 0 &&
                    rect.right >= 0
                );
            };

            // 加载图片的函数
            const loadImage = (image) => {
                if (image.dataset.src) {
                    image.src = image.dataset.src;
                    image.setAttribute("lazy", "loaded");
                    image.removeAttribute('data-src');
                }
            };

            // IntersectionObserver 回调函数
            const onIntersection = (entries) => {
                entries.forEach((entry) => {
                    if (entry.isIntersecting) {
                        loadImage(entry.target);
                        observer.unobserve(entry.target);//加载成功后,移除监听对象
                    }
                });
            };

            // 创建 IntersectionObserver 实例
            const observer = new IntersectionObserver(onIntersection, {
                root: null,
                rootMargin: '0px',
                threshold: 0,
            });

            // 观察每个图片元素
            images.forEach((image) => {
                if (isInViewport(image)) {
                    loadImage(image);
                } else {
                    observer.observe(image);
                }
            });
        }

        // 在 DOMContentLoaded 事件触发时调用懒加载函数
        document.addEventListener('DOMContentLoaded', lazyLoadImages);
        </script>

    <body style="display: flex;flex-direction: column;">
            <img lazy='loading' src="https://www.minigame.vip/static/images/lazy-img.png" data-src="https://res.minigame.vip/gc-assets/bubble-shooter/bubble-shooter_banner.webp" style="height: 300px;width: 600px;">
            <img lazy='loading' src="https://www.minigame.vip/static/images/lazy-img.png" data-src="https://res.minigame.vip/gc-assets/bubble-shooter/bubble-shooter_banner.webp" style="height: 300px;width: 600px;">
            <img lazy='loading' src="https://www.minigame.vip/static/images/lazy-img.png" data-src="https://res.minigame.vip/gc-assets/bubble-shooter/bubble-shooter_banner.webp" style="height: 300px;width: 600px;">
            <img lazy='loading' src="https://www.minigame.vip/static/images/lazy-img.png" data-src="https://res.minigame.vip/gc-assets/bubble-shooter/bubble-shooter_banner.webp" style="height: 300px;width: 600px;">
            <img lazy='loading' src="https://www.minigame.vip/static/images/lazy-img.png" data-src="https://res.minigame.vip/gc-assets/bubble-shooter/bubble-shooter_banner.webp" style="height: 300px;width: 600px;">
            <img lazy='loading' src="https://www.minigame.vip/static/images/lazy-img.png" data-src="https://res.minigame.vip/gc-assets/bubble-shooter/bubble-shooter_banner.webp" style="height: 300px;width: 600px;">
            <img lazy='loading' src="https://www.minigame.vip/static/images/lazy-img.png" data-src="https://res.minigame.vip/gc-assets/bubble-shooter/bubble-shooter_banner.webp" style="height: 300px;width: 600px;">
            <img lazy='loading' src="https://www.minigame.vip/static/images/lazy-img.png" data-src="https://res.minigame.vip/gc-assets/bubble-shooter/bubble-shooter_banner.webp" style="height: 300px;width: 600px;">
            <img lazy='loading' src="https://www.minigame.vip/static/images/lazy-img.png" data-src="https://res.minigame.vip/gc-assets/bubble-shooter/bubble-shooter_banner.webp" style="height: 300px;width: 600px;">
            <img lazy='loading' src="https://www.minigame.vip/static/images/lazy-img.png" data-src="https://res.minigame.vip/gc-assets/bubble-shooter/bubble-shooter_banner.webp" style="height: 300px;width: 600px;">
    </body>

3、结果

两种方式的结果是一样的

  • 1、旧的方式需要多次绑定事件触发,且会存在部分元素重复绑定事件,没有兼容性问题。
  • 2、使用IntersectionObserver触发的方式比较统一,且只要设置一次,对于在首屏资源中加载,优势也比较大,可以等其他重要资源加载好之后,再启动该方法执行全局图片资源懒加载。既可以保证速度也可以保证效果。缺点是部分老旧的浏览器会存在兼容性问题,可以使用polyfill处理

953g4-zcbfw.gif