图片懒加载组件

136 阅读3分钟

什么是图片图片懒加载呢,顾名思义,惰性加载。 原理: 非一次性加载全部图片,会造成过多图片网络请求占用不必要的资源。

比如说,一个图片网站中首页存在几千张图片,如果一次性加载就是同时请求了几千张图片肯定慢的不行,但在用户视角中只会看到十几或几十张图片(浏览器窗口就那么大嘛),其他几千张图片根本看不到,这时候加载了的意义不大。

这种情况一般两种解决方案: 1、分页请求 2、图片懒加载。 其实说起来根源一样,那就是减少请求的数量从而减少加载时间,最终以更短的时间来呈现效果,这里主要来探讨图片懒加载的方案

图片懒加载

目前实现的方案较多:

img的loading属性设为“lazy”

offsetTop来计算图片位置

getBoundingClientRect来计算是否在可视区域内

IntersectionObserver监听可视区域

...等等

反正大家核心点都相同,只是使用不同的实现方式,这里我挑选其中比较简单的IntersectionObserver来实现了

IntersectionObserver

什么是IntersectionObserver?

可以点击自行去mdn查看具体详情,这里我的肤浅理解是:监听元素是否出现在指定元素的可视范围。 以图片为例: 监听当前浏览器窗口可看到的图片 (当然还可以自行设置视口偏移量或指定元素视口而非默认的浏览器视口等)

image.png

非特别设置情况: 监听红色框区域内的图片,黄色框中的图片的监听是否会触发呢,是的,因为它已经进入了视口,所以也会触发监听,而紫色区域的图片还未进入视口也就未触发到监听了。

接下来以代码的形式实现一下:

<template>
  <div ref="imgContainer">
    <img v-if="isIntersecting" :src="src" :alt="alt" />
  </div>
</template>

<script>
import { ref, onMounted, onBeforeUnmount, defineComponent } from "vue";

export default defineComponent({
  name: "LazyLoadImage",
  props: {
    src: {
      type: String,
      required: true,
    },
    alt: {
      type: String,
      default: "",
    },
  },
  setup(props) {
    const imgContainer = ref(null);
    const isIntersecting = ref(false);
    let observer;

    const loadImage = () => {
      isIntersecting.value = true;
    };

    const createObserver = () => {
      observer = new IntersectionObserver((entries) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting) {
            loadImage();
            observer.unobserve(entry.target);
          }
        });
      });
      if (imgContainer.value) {
        observer.observe(imgContainer.value);
      }
    };

    onMounted(() => {
      createObserver();
    });

    onBeforeUnmount(() => {
      if (observer && imgContainer.value) {
        observer.unobserve(imgContainer.value);
      }
    });

    return {
      imgContainer,
      isIntersecting,
    };
  },
});
</script>

整体实现: 在组件加载完成后创建IntersectionObserver的监听器,监听器中需要传递一个回调函数和配置项(可不传),其中回调函数的entries就是当前触发监听元素的列表,如上图也就是红框和黄框的图片,当触发回调函数后,对这些图片遍历判断是否处于视口中,对处于视口的图片才让其显示,然后移除其对应的监听器(否则滚动时会一直触发)

这里只是一个思路,常规项目中并不是以v-if方式,而是默认设置占位图片(src="占位图地址"),当监听被触发后在回调函数中动态修改图片的src为真实图片路径从而加载对应图片。

对了,监听器还有配置项,使用方式:

new IntersectionObserver((entries) => { ... }, {
    root: imgContainer.value, // 监听的元素(dom)
    rootMargin: 10px, // 偏移量
    threshold: [0, ..., 100] // 监听元素的可见比例,每达到设置的可见比例都会触发监听回调
})

image.png

关于其兼容性: 基本上浏览器都支持,ie,嗯当没看到

image.png

总结

图片懒加载: 减少加赞量,减少加赞时间,加快加载响应速度,具体看你对各个粒度的追求和把控


编程的世界总是充满未知,期待与你在技术的海洋中再相遇
I just hope my code makes more sense than my life.