如何实现图片懒加载

2024-09-27 4 0

图片懒加载

1.懒加载是什么

图片进入到可视区发起网络请求获取图片。

1.1为什么要懒加载

懒加载是一种网页性能优化的方式,它能极大的提升用户体验。就比如说图片,图片一直是影响网页性能的主要元凶,现在一张图片超过几兆已经是很经常的事了。如果每次进入页面就请求所有的图片资源,那么可能等图片加载出来用户也早就走了。所以,我们需要懒加载,进入页面的时候,只请求可视区域的图片资源。

1.2懒加载的实现原理?

一张图片就是一个标签,而图片的来源主要是src属性。浏览器是否发起亲求就是根据是否有src属性决定的。 既然这样,那么我们就要对标签的src属性下手了,在没进入可视区域的时候,我们先不给这个标签赋src属性,这样岂不是浏览器就不会发送请求了。

2.如何判断图片是否进入了可视区

元素到可视区顶部的距离小于可视区的高度

元素到可视区顶部的距离等于元素到浏览器顶部的距离-滚动条的距离或者使用getBoundingClientRect()方法获取

2.1第一种实现方式

获取传递过来的图片地址,通过html5的自定义属性,将src赋值到data-src上。 通过监听滚动获取元素的位置,如果图片出现在可视区,获取到img元素的data-src属性,赋值到src上。

import React from "react";

const Index = (props) => {
    const { src } = props;
    const imgRef = React.useRef();
    React.useEffect(() => {
        const { current } = imgRef;
        const isVisable = () => {
            const { top } = current.getBoundingClientRect();
            if (top <= window.innerHeight) {
                current.src = current.getAttribute("data-src");
            }
        };
        isVisable();
        window.addEventListener("scroll", function () {
            isVisable();
        });
    }, []);

    return <img ref={imgRef} src="" alt="" data-src={src} width="100%" height="100%" />;
};

export default Index;

2.2第二种实现方式

传统的实现方法是,监听到scroll事件后,调用目标元素(绿色方块)的 getBoundingClientRect() 方法,得到它对应于视口左上角的坐标,再判断是否在视口之内。这种方法的缺点是,由于scroll事件密集发生,计算量很大,容易造成性能问题。

目前有一个新的 IntersectionObserver API ,可以自动"观察"元素是否可见,Chrome 51+ 已经支持。由于可见(visible)的本质是,目标元素与视口产生一个交叉区,所以这个 API 叫做"交叉观察器"。

IntersectionObserver对象提供目标元素的信息,一共有六个属性。

{
  time: 3893.92,
  rootBounds: ClientRect {
    bottom: 920,
    height: 1024,
    left: 0,
    right: 1024,
    top: 0,
    width: 920
  },
  boundingClientRect: ClientRect {
     // ...
  },
  intersectionRect: ClientRect {
    // ...
  },
  intersectionRatio: 0.54,
  target: element
}

每个属性的含义如下。

time:可见性发生变化的时间,是一个高精度时间戳,单位为毫秒
target:被观察的目标元素,是一个 DOM 节点对象
rootBounds:根元素的矩形区域的信息,getBoundingClientRect()方法的返回值,如果没有根元素(即直接相对于视口滚动),则返回null
boundingClientRect:目标元素的矩形区域的信息
intersectionRect:目标元素与视口(或根元素)的交叉区域的信息
intersectionRatio:目标元素的可见比例,即intersectionRect占boundingClientRect的比例,完全可见时为1,完全不可见时小于等于0

import React from "react";

const Index = (props) => {
    const { src } = props;
    const imgRef = React.useRef();
    React.useEffect(() => {
        const { current } = imgRef;
        const io = new IntersectionObserver(entries=>{
            entries.forEach(item=>{
                if (item.intersectionRatio <= 0) return;
                current.src = current.getAttribute("data-src");
                io.unobserve(current)
            })
        })
        io.observe(current)

    }, []);

    return <img ref={imgRef} src="" alt="" data-src={src} width="100%" height="100%" />;
};

export default Index;

参考文章