vue3 图片懒加载组件

介绍

css 新属性

content-visibility:控制元素是否渲染其内容,以及强制一组强大的包含,允许用户代理潜在地省略大量布局和渲染工作,直到需要它为止。基本上,它使用户代理能够在需要之前跳过元素的渲染工作(包括布局和绘画)——这使得初始页面加载速度更快。

/* Keyword values */
content-visibility: visible;  //没有效果。元素的内容被正常布局和渲染。
content-visibility: hidden; // 该元素跳过其内容。用户代理功能(例如在页面中查找、标签顺序导航等)不能访问跳过的内容,也不能选择或聚焦。这类似于给出内容display: none。
content-visibility: auto; // 该元素打开布局包含、样式包含和绘制包含。如果元素与用户无关,它也会跳过其内容。与隐藏不同,跳过的内容必须仍然对用户代理功能(如在页面中查找、标签顺序导航等)正常可用,并且必须正常可聚焦和可选择。

注意:兼容性差,fireFox,safari不支持

IntersectionObserver

使用场景:图片多、大的情况,防止一次性加载所有图片,在图片未进入可视页面之前,img 表情没有 scr 属性,(如果有 scr,即使为空,也会发送请求)
immediatly:是否立即加载图片,打开页面,默认在可视区域内的需要将其设置成 true
img.complete:图片已经加载过该文件时,此时可以直接显示出来

intersection-observer-polyfill: 可以兼容低版本浏览器

<template>
  <img v-if="!immediately" :data-src="src" class="opacity0" ref="imgElement" />
  <img v-else :src="src" class="opacity0" ref="imgElement" />
</template>

<script lang="ts">
import { defineComponent, Ref, ref, onMounted } from 'vue';
import IntersectionObserver from 'intersection-observer-polyfill';

export default defineComponent({
  name: 'LazyImg',
  props: {
    src: {
      default: '',
      required: true,
    },
    immediately: {
      default: false,
    },
  },
  setup(props) {
    const imgElement: Ref<HTMLImageElement | null> = ref(null);
    const lazyload = (target: HTMLElement) => {
      const io = new IntersectionObserver((entries: any[], observer: any) => {
        entries.forEach((entry) => {
          // isIntersecting 企微不支持(使用了polyfill 也这样)
          if (entry?.isIntersecting || entry.intersectionRatio > 0) {
            const img = entry.target;
            const src = img.getAttribute('data-src');
            img.setAttribute('src', src);
            img.removeAttribute('data-src');
            observer.disconnect();
          }
        });
      });
      io.observe(target);
    };

    onMounted(() => {
      const img = imgElement.value;
      if (img) {
        if (!props.immediately) {
          lazyload(img);
        }
        if (img.complete) {
          // 如果图片已经存在于浏览器缓存,直接显示出来
          img.classList.remove('opacity0');
          img.classList.add('fade');
        } else {
          img.onload = () => {
            img.classList.remove('opacity0');
            img.classList.add('fade');
          };
        }

        img.onerror = () =>
          setTimeout(() => img.setAttribute('src', props.src), 500);
      }
    });

    return {
      imgElement,
    };
  },
});
</script>