如何实现图片懒加载,原生 + React 实现方式

前言

有时候列表存在许多图片,那么一次性加载会阻塞 http 请求,为了避免在可视窗口之外的元素进行不必要的图片加载,可以尝试使用懒加载进行优化。懒加载可以显著提高页面加载性能,特别是当页面包含大量图片时。为了实现延迟加载图片(也称为懒加载),可以使用 JavaScript 和 Intersection Observer API。

实现

步骤

  1. HTML 结构 :为每个图片元素设置一个占位符,并使用 data-src 属性存储实际的图片 URL。
  2. CSS 样式:设置图片占位符的样式。
  3. JavaScript:使用 Intersection Observer API 监控图片元素,当图片元素进入视口时,加载实际的图片。

代码示例

HTML 结构
html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Lazy Load Images</title>
  <style>
    .placeholder {
      width: 100%;
      height: 200px;
      background-color: #f0f0f0;
      display: flex;
      align-items: center;
      justify-content: center;
      color: #ccc;
    }
  </style>
</head>
<body>
  <h1>Lazy Load Images Example</h1>
  <div class="image-container">
    <img class="lazy" data-src="image1.jpg" alt="Image 1" class="placeholder">
    <img class="lazy" data-src="image2.jpg" alt="Image 2" class="placeholder">
    <img class="lazy" data-src="image3.jpg" alt="Image 3" class="placeholder">
    <!-- 添加更多图片 -->
  </div>

  <script>
    document.addEventListener("DOMContentLoaded", function() {
      const lazyImages = document.querySelectorAll('img.lazy');

      if ('IntersectionObserver' in window) {
        const lazyImageObserver = new IntersectionObserver(function(entries, observer) {
          entries.forEach(function(entry) {
            if (entry.isIntersecting) {
              const lazyImage = entry.target;
              lazyImage.src = lazyImage.dataset.src;
              lazyImage.classList.remove('lazy');
              lazyImageObserver.unobserve(lazyImage);
            }
          });
        });

        lazyImages.forEach(function(lazyImage) {
          lazyImageObserver.observe(lazyImage);
        });
      } else {
        // Fallback for browsers that do not support IntersectionObserver
        let lazyLoadThrottleTimeout;
        function lazyLoad() {
          if (lazyLoadThrottleTimeout) {
            clearTimeout(lazyLoadThrottleTimeout);
          }

          lazyLoadThrottleTimeout = setTimeout(function() {
            const scrollTop = window.pageYOffset;
            lazyImages.forEach(function(img) {
              if (img.offsetTop < (window.innerHeight + scrollTop)) {
                img.src = img.dataset.src;
                img.classList.remove('lazy');
              }
            });
            if (lazyImages.length == 0) {
              document.removeEventListener("scroll", lazyLoad);
              window.removeEventListener("resize", lazyLoad);
              window.removeEventListener("orientationChange", lazyLoad);
            }
          }, 20);
        }

        document.addEventListener("scroll", lazyLoad);
        window.addEventListener("resize", lazyLoad);
        window.addEventListener("orientationChange", lazyLoad);
      }
    });
  </script>
</body>
</html>
解释
  1. HTML 结构
    • 使用 data-src 属性存储实际的图片 URL。
    • 使用 class="lazy" 标记需要懒加载的图片。
  2. CSS 样式
    • 设置图片占位符的样式,确保在图片加载前显示占位符。
  3. JavaScript
    • 使用 IntersectionObserver 监控图片元素,当图片元素进入视口时,加载实际的图片。
    • 如果浏览器不支持 IntersectionObserver,使用滚动事件和节流函数实现懒加载。

React 代码实例

代码结构
  1. 创建 React 组件
javascript 复制代码
import React, { useEffect, useRef } from 'react';
import './App.css';

const LazyImage = ({ src, alt }) => {
  const imgRef = useRef();

  useEffect(() => {
    const imgElement = imgRef.current;

    const handleIntersection = (entries, observer) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          const lazyImage = entry.target;
          lazyImage.src = lazyImage.dataset.src;
          lazyImage.classList.remove('lazy');
          observer.unobserve(lazyImage);
        }
      });
    };

    const observer = new IntersectionObserver(handleIntersection, {
      root: null, // 使用视口作为根
      rootMargin: '0px',
      threshold: 0.1 // 当至少 10% 的图片进入视口时触发
    });

    if (imgElement) {
      observer.observe(imgElement);
    }

    return () => {
      if (imgElement) {
        observer.unobserve(imgElement);
      }
    };
  }, []);

  return <img ref={imgRef} data-src={src} alt={alt} className="lazy placeholder" />;
};

const App = () => {
  return (
    <div className="App">
      <h1>Lazy Load Images Example</h1>
      <div className="image-container">
        <LazyImage src="https://via.placeholder.com/300" alt="Image 1" />
        <LazyImage src="https://via.placeholder.com/300" alt="Image 2" />
        <LazyImage src="https://via.placeholder.com/300" alt="Image 3" />
        {/* 添加更多图片 */}
      </div>
    </div>
  );
};

export default App;
  1. 添加 CSS 样式
    在 App.css 文件中添加以下样式:
css 复制代码
.placeholder {
  width: 100%;
  height: 200px;
  background-color: #f0f0f0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #ccc;
}
解释
  1. LazyImage 组件
    • 使用 useRef 获取图片元素的引用。
    • 使用 useEffect 在组件挂载时创建 IntersectionObserver 实例,并监控图片元素。
    • 当图片元素进入视口时,加载实际的图片,并取消对该图片元素的监控。
  2. App 组件
    • 渲染多个 LazyImage 组件,每个组件对应一张需要懒加载的图片。
  3. CSS 样式
    • 设置图片占位符的样式,确保在图片加载前显示占位符。
调试步骤
  1. 检查图片元素是否被正确观察
    • 在 useEffect 中添加 console.log(imgElement),确保图片元素被正确获取。
  2. 检查 IntersectionObserver 的回调
    • 在 handleIntersection 函数中添加 console.log(entries),确保回调函数被正确调用。
  3. 检查图片元素是否进入视口
    • 确保页面布局正确,图片元素确实进入了视口。

兼容性

  • IntersectionObserver 是现代浏览器支持的 API,如果需要兼容旧版浏览器,可以使用滚动事件和节流函数作为回退方案。

这样,当用户滚动页面时,只有进入视口的图片才会被加载,从而提高页面的加载性能。

总结

但这样不可避免的会存在一定视觉效果上的体验缺失,在页面滚动特别快速时,由于浏览器来不及绘制刚刚进入视图的元素,便会导致出现短暂的白屏现象。这便需要在开发过程中,去做出一定地取舍。

相关推荐
敲啊敲952726 分钟前
5.npm包
前端·npm·node.js
brrdg_sefg40 分钟前
Rust 在前端基建中的使用
前端·rust·状态模式
m0_748230941 小时前
Rust赋能前端: 纯血前端将 Table 导出 Excel
前端·rust·excel
qq_589568101 小时前
Echarts的高级使用,动画,交互api
前端·javascript·echarts
黑客老陈2 小时前
新手小白如何挖掘cnvd通用漏洞之存储xss漏洞(利用xss钓鱼)
运维·服务器·前端·网络·安全·web3·xss
正小安2 小时前
Vite系列课程 | 11. Vite 配置文件中 CSS 配置(Modules 模块化篇)
前端·vite
暴富的Tdy3 小时前
【CryptoJS库AES加密】
前端·javascript·vue.js
neeef_se3 小时前
Vue中使用a标签下载静态资源文件(比如excel、pdf等),纯前端操作
前端·vue.js·excel
m0_748235613 小时前
web 渗透学习指南——初学者防入狱篇
前端