加载大量图片优化 - 懒加载

102 阅读1分钟

懒加载

方案1: 子元素.offsetTop - 父元素.offsetTop <= 父元素.clientHeight + 父元素.scrollTop

缺点:滚动触发事件时,需要执行大量的循环判断

<template>
  <div class="menu-contain card">
    <div class="img-list">
      <div class="img-box" v-for="(item, index) in imgList" :key="index">
        <img
          class="img-item"
          src="@/assets/images/loading.gif"
          :data-src="item"
          alt=""
        />
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { throttle } from "@/utils";
import {ref, nextTick } from "vue";

const imgList = ref<string[]>([]);
const imgElementList = ref<NodeListOf<HTMLImageElement>>()
const boxElement = ref<HTMLDivElement>()
setTimeout(async () => {
  imgList.value = [
    "https://img0.baidu.com/it/u=3001507999,2508317196&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500",
    "https://img1.baidu.com/it/u=1365309735,3244631182&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500",
    "https://img2.baidu.com/it/u=3520579559,1414123857&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
    ...
  ];

  await nextTick()
  imgElementList.value = document.querySelectorAll(".img-item");

  boxElement.value = document.querySelector('.img-list') as HTMLDivElement
  handlerLoadImg()

  boxElement.value.addEventListener('scroll', throttle(handlerLoadImg, 1000, false, true))
}, 1000);

// 方案1
const handlerLoadImg = () => {
  imgElementList.value?.forEach((item) => {
    if(item.offsetTop - boxElement.value!.offsetTop <= boxElement.value!.clientHeight + boxElement.value!.scrollTop ) {
      item.setAttribute('src', item.getAttribute('data-src') as string)
    }
  })
}
</script>

<style scoped lang="scss">
.menu-contain {
  height: 100%;
}
.img-list {
  display: flex;
  flex-wrap: wrap;
  width: 100%;
  height: 100%;
  overflow: auto;
  .img-box {
    padding: 10px;
    width: 20%;
    height: fit-content;
    .img-item {
      width: 100%;
      height: 100%;
    }
  }
}
</style>

方案2:IntersectionObserver

<template>
  <div class="menu-contain card">
    <div class="img-list">
      <div class="img-box" v-for="(item, index) in imgList" :key="index">
        <img
          class="img-item"
          src="@/assets/images/loading.gif"
          :data-src="item"
          alt=""
        />
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import {ref, nextTick } from "vue";

const imgList = ref<string[]>([]);
const imgElementList = ref<NodeListOf<HTMLImageElement>>()
setTimeout(async () => {
  imgList.value = [
    "https://img0.baidu.com/it/u=3001507999,2508317196&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500",
    "https://img1.baidu.com/it/u=1365309735,3244631182&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500",
    "https://img2.baidu.com/it/u=3520579559,1414123857&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
    ...
  ];

  await nextTick()
  imgElementList.value = document.querySelectorAll(".img-item");

  // 方案2
  const observer = new IntersectionObserver((entries) => {
    entries.forEach(item => {
      if(item.isIntersecting) {
        const targetImg = item.target
        targetImg.setAttribute('src', targetImg.getAttribute('data-src') as string)
      }
    })
  })
  imgElementList.value.forEach(item => {
    observer.observe(item)
  })
}, 1000);
</script>

<style scoped lang="scss">
.menu-contain {
  height: 100%;
}
.img-list {
  display: flex;
  flex-wrap: wrap;
  width: 100%;
  height: 100%;
  overflow: auto;
  .img-box {
    padding: 10px;
    width: 20%;
    height: fit-content;
    .img-item {
      width: 100%;
      height: 100%;
    }
  }
}
</style>