React 实现图片懒加载的常见方式

前言

图片懒加载是一种优化网页性能的技术,它允许在用户滚动到图片位置之前延迟加载图片。通过懒加载,可以在用户需要查看图片时才加载图片,避免了不必要的图片加载,从而提高了网页的加载速度和用户体验。

方案一

实现思路

在说明思路之前,先了解几个常见的视图属性。

  1. clientHeight:可视区域的高度,对应的也就是下图中的滚动区域。
  2. scrollTop:滚动条滚动的高度,它指的是内容区的顶部到可视区域顶部的距离。
  3. offsetTop:元素到offsetParent顶部的距离。
  4. offsetParent:距离元素最近的一个具有定位的祖宗元素(relative,absolute,fixed),若祖宗都不符合条件,offsetParent为body。

图解:

根据上面的图解可知,当图片的滚动条滚动的高度加上可视区域的高度大于当前的图片的offsetTop,那么说明图片正在进入可视区域。这个时候便可以加载当前图片。

第一步

模拟后台返回的图片url,遍历产生一个url集合,用于后面的懒加载使用。

js 复制代码
const imgUrls = (num = 10) => {
  const urls = [];
  for (let i = 0; i < num; i++) {
    const url = `https://robohash.org/${i}.png`;
    urls.push(url);
  }
  return urls;
};

第二步

遍历图片url集合,渲染100张loading图片,css部分省略。

html 复制代码
    <div className={styles['box-one']} ref={scrollRef}>
      {imgUrls(100).map((item) => {
        return <img data-src={item} key={item} src={loadingUrl} alt="" />;
      })}
    </div>

效果预览

第三步

监听容器的滚动事件,当容器滚动时计算容器的高度加上滚动条的高度大于当前图片的offsetTop时加载当前的图片。完整代码如下:

tsx 复制代码
import loadingUrl from '@/assets/imgs/loading.jpg';
import { useEffect, useRef } from 'react';
import styles from '../index.less';

// 图片url
const imgUrls = (num = 10) => {
  const urls = [];
  for (let i = 0; i < num; i++) {
    const url = `https://robohash.org/${i}.png`;
    urls.push(url);
  }
  return urls;
};

const LazyLoading = () => {
  const scrollRef = useRef({} as any);

  // 滚动事件
  const changeScroll = () => {
    const clientHeight = scrollRef?.current.clientHeight; //可视区域高度
    const scrollTop = scrollRef?.current.scrollTop; //滚动条滚动高度
    const childNodes = scrollRef?.current.childNodes; // 获取所有图片集合

    for (let j = 0; j < childNodes.length; j++) {
      const element = childNodes[j];
      if (scrollTop + clientHeight > element.offsetTop) {
        element.src = element.getAttribute('data-src'); // 替换当前的src
      }
    }
  };

  useEffect(() => {
    changeScroll(); // 第一次渲染的时候替换loading图片
  }, []);

  return (
    <div className={styles['box-one']} ref={scrollRef} onScroll={changeScroll}>
      {imgUrls(100).map((item) => {
        return <img data-src={item} key={item} src={loadingUrl} alt="" />;
      })}
    </div>
  );
};

export default LazyLoading;

效果预览

方案二

实现思路

方案二的实现思路利用浏览器提供的 IntersectionObserver API实现。IntersectionObserver API提供了一种方便的方式来监视目标元素和其祖先元素或视窗之间的交叉状态变化。当目标元素进入或离开视口时,可以触发回调函数,进行相应的操作。它的原理是通过注册一个回调函数来观察特定元素的交叉状态变化,并在满足条件时执行相应的操作。

使用 IntersectionObserver API非常简单,可以通过创建一个 IntersectionObserver 实例,并传入回调函数和选项对象来实现。回调函数会在目标元素的交叉状态发生变化时被调用,并接收一个参数,包含有关交叉状态的信息。

实现完整代码

tsx 复制代码
import loadingUrl from '@/assets/imgs/loading.jpg';
import styles from '../index.less';
import React, { useRef, useEffect, useState } from 'react';
// 图片url
const imgUrls = (num = 10) => {
  const urls = [];
  for (let i = 0; i < num; i++) {
    const url = `https://robohash.org/${i}.png`;
    urls.push(url);
  }
  return urls;
};

const LazyLoadImage = ({ src, alt }) => {
  const [imageSrc, setImageSrc] = useState(loadingUrl);
  const imgRef = useRef(null as any);

  useEffect(() => {
    let observer: IntersectionObserver;
    if (imgRef.current) {
      // 创建IntersectionObserver实例
      observer = new IntersectionObserver(
        ([entry]) => {
          // 当图片进入可视区域时,设置图片地址进行加载
          if (entry.isIntersecting) {
            setImageSrc(src);
            observer.unobserve(imgRef.current);
          }
        },
        {
          rootMargin: '0px 0px 200px 0px', // 可视区域的上边距设置为200px
        },
      );
      observer.observe(imgRef.current); //开始观察目标元素
    }
    return () => {
      if (observer && observer.unobserve) {
        observer.unobserve(imgRef.current);
      }
    };
  }, [src]);

  return <img ref={imgRef} src={imageSrc} alt={alt} />;
};

const LazyLoading = () => {
  return (
    <div className={styles['box-two']}>
      {imgUrls(100).map((item) => {
        return <LazyLoadImage src={item} alt="lazy load image" />;
      })}
    </div>
  );
};

export default LazyLoading;

实现效果

⚠️注意事项

在初始化的时候,需要给imageSrc设置一个初始化的loading地址,如果没有的话,初始化的时候会加载多张图片。

方案三

实现思路

利用react的懒加载库react-lazyload,在使用之前需要先安装 yarn add react-lazyload,这里介绍几个它的常见属性:

  1. scrollContainer: 指定的滚动的区域,默认值是undefined,如果没有指定默认是窗口的视图作为滚动区域。
  2. offset: 元素距离视口顶部的距离,当达到这个距离时,元素将被加载。
  3. scroll: 是否监听滚动
  4. height: 渲染元素的占位符的高度。
  5. overflow : 如果溢出容器,延迟加载组件

代码实现

因为这里实现的图片懒加载是局部懒加载,所以需要指定 scrollContainerscrollContainer 的值DOM对象。在实现的过程中,同时需要设置overflow为true,以及height的值。

tsx 复制代码
import react, { useRef, useEffect } from 'react';
import LazyLoad from 'react-lazyload';
import styles from '../index.less';

// 图片url
const imgUrls = (num = 10) => {
  const urls = [];
  for (let i = 0; i < num; i++) {
    const url = `https://robohash.org/${i}.png`;
    urls.push(url);
  }
  return urls;
};

const LazyLoading = () => {
  const scrollRef = useRef({} as any);

  return (
    <div className={styles['box-three']} ref={scrollRef}>
      {imgUrls(100).map((item) => {
        return (
          <LazyLoad
            height={200}
            overflow={true}
            offset={0}
            key={item}
            scroll={true}
            scrollContainer={scrollRef.current} // DOM
          >
            <img src={item} alt="" />
          </LazyLoad>
        );
      })}
    </div>
  );
};

export default LazyLoading;

实现效果

相关推荐
多多*7 分钟前
Spring之Bean的初始化 Bean的生命周期 全站式解析
java·开发语言·前端·数据库·后端·spring·servlet
linweidong12 分钟前
在企业级应用中,你如何构建一个全面的前端测试策略,包括单元测试、集成测试、端到端测试
前端·selenium·单元测试·集成测试·前端面试·mocha·前端面经
满怀101531 分钟前
【HTML 全栈进阶】从语义化到现代 Web 开发实战
前端·html
东锋1.343 分钟前
前端动画库 Anime.js 的V4 版本,兼容 Vue、React
前端·javascript·vue.js
满怀10151 小时前
【Flask全栈开发指南】从零构建企业级Web应用
前端·python·flask·后端开发·全栈开发
小杨升级打怪中1 小时前
前端面经-webpack篇--定义、配置、构建流程、 Loader、Tree Shaking、懒加载与预加载、代码分割、 Plugin 机制
前端·webpack·node.js
每次的天空1 小时前
Android第三次面试总结之网络篇补充
android·网络·面试
Yvonne爱编码2 小时前
CSS- 4.4 固定定位(fixed)& 咖啡售卖官网实例
前端·css·html·状态模式·hbuilder
SuperherRo2 小时前
Web开发-JavaEE应用&SpringBoot栈&SnakeYaml反序列化链&JAR&WAR&构建打包
前端·java-ee·jar·反序列化·war·snakeyaml
大帅不是我2 小时前
Python多进程编程执行任务
java·前端·python