使用 React Hooks 创建延迟加载图像组件

2025-05-28

使用 React Hooks 创建延迟加载图像组件

延迟加载图片(例如 Medium 中的图片或gatsby-image 🧡 创建的图片)有时可以为页面增添一抹额外的风格。要实现这种效果,需要:A)一个用于预览的缩略图,最好以 data URL 的形式内联;B)设置图片的宽高比,用于创建占位符以防止回流。在本文中,我将分享如何使用 React Hooks 创建一个延迟加载的图片组件。

CodeSandbox 演示

嗨,Dev.to 的开发者们!很高兴成为 DEV 社区的一员,这是我的第一篇博文。

首先,基本框架 - HTML/CSS🦴

通常,延迟加载图像由 4 个 HTML 元素组成:

<div class="wrapper">
  <div style="padding-bottom:76%;"></div> 
  <img
    src="https://images.unsplash.com/photo-1518991791750-044b923256f0?fit=crop&w=25"
  />
  <img
    src="https://images.unsplash.com/photo-1518991791750-044b923256f0?fit=crop&w=1200"
    class="source"
  />
</div>
  1. 相对定位的包装器div
  2. 用于保持宽高比的固有占位div符。它具有 padding-bottom 属性,其值为百分比值(相对于包含块的宽度)。例如,对于 16:9 的图像,其百分比计算为 9/16 * 100% = 56.25%。
  3. 绝对定位img,用于图片的微小版本,也称为 LQIP(质量图片占位) ,拉伸以覆盖包装。数据 URL 通常用作 src 来保存 HTTP 请求,
  4. 绝对定位的img源图像,放置在 LQIP 之上,用 初始化opacity: 0
.wrapper {
  position: relative;
  overflow: hidden;
}
img {
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  object-fit: cover;
  object-position: center;
}
.source {
  opacity: 0;
  transition: opacity 1s;
}
.loaded {
  opacity: 1;
}

将其转换为 React 组件⚛

import React, { useState, useEffect, useRef } from "react";
import clsx from "clsx"; // a utility for constructing className conditionally
function LazyImage({ className, src, alt, lqip, aspectRatio = 2/3 }) {
  const [loaded, setLoaded] = useState(false);
  const imgRef = useRef();
  useEffect(() => {
    if (imgRef.current && imgRef.current.complete) {
      setLoaded(true);
    }
  }, []);
  return (
    <div className={clsx("wrapper", className)}>
      <div style={{ paddingBottom: `${100 / aspectRatio}%` }} />
      <img src={lqip} aria-hidden="true" />
      <img
        loading="lazy"
        src={src}
        alt={alt}
        ref={imgRef}
        onLoad={() => setLoaded(true)}
        className={clsx("source", loaded && "loaded")}
      />
    </div>
  );
}
export default LazyImage;

让我们分解一下:有一个 loaded 状态用于跟踪loading源图像的状态,初始化为 false。在源img元素中添加了一个 "load" 事件监听器,因此当源图像加载完成时,状态会更新,并在其类列表中添加一个 "loaded" 类名,将其不透明度设置为 1。如果源图像在组件挂载之前已完全加载,则新添加的 "load" 事件监听器将永远不会触发。因此,还会向元素传递一个 ref,img用于在挂载时检查其 complete 属性,并相应地更新状态。

此外,loading="lazy"还向源文件添加了一个属性,img用于指示浏览器如果图像位于视口中则立即加载该图像,否则当用户滚动到该图像附近时获取该图像。有关更多信息,请参阅这篇web.dev 文章。我还在aria-hidden="true"LQIP 中添加了一项功能img,以便将其隐藏在辅助功能 API 中

用法

要使用此组件,您必须生成图像 LQIP 并获取其宽高比。有一些库可以帮助您将生成过程集成到构建过程中,例如zouhir/lqip。显然,如果您使用 Cloudindary,则可以通过其图像转换管道创建 LQIP。但我怀疑您只能获取常规 URL,而不是数据 URL 或 base64,因此如果您想内联它,可能需要自行转换。

在之前的项目中,我使用了Next.js 中的sharp(一个高性能图像处理模块)getStaticProps(一个在构建时运行的静态生成的函数)来帮助我填充这些图像数据。下面是我使用的函数:

import got from 'got'; // HTTP request library for Node.js
import sharp from 'sharp';
sharp.cache(false);
async function generateLazyImage(src) {
  const { body } = await got(src, { responseType: 'buffer' });
  const sharpImage = sharp(body);
  const { width, height, format } = await sharpImage.metadata();
  const lqipBuf = await sharpImage
    .resize({ width: 30, height: 30, fit: 'inside' })
    .toBuffer();
  return {
    src,
    aspectRatio: width / height,
    lqip: `data:image/${format};base64,${lqipBuf.toString('base64')}`,
  };
}

就是这样!这<LazyImage />是一个非常简单的组件,我几乎在所有项目中都会用到它。欢迎分享你的想法以及你在网站上如何呈现图片。😉

如果您想阅读我以后的帖子,请关注我的 Twitter 账号。我保证很快就会找到如何使用 Next.js 实现 RSS……(2020 年 6 月 25 日更新:我的博客现在有 RSS 源了。✅)

React 峰会

React 峰会将于 10 月 15 日至 16 日回归。届时将有Kent C. DoddsMax StoiberSara VieiraSharif Shameem等嘉宾发表演讲。

9 月 20 日前免费注册:https://ti.to/gitnation/react-summit?source =REFURCR-1 。

文章来源:https://dev.to/hangindev/create-a-lazy-loading-image-component-with-react-hooks-4p19
PREV
我希望早点知道的 VS Code 快捷方式和技巧简介 VS Code 快捷方式 VS Code 源代码控制选项卡 有用的 VS Code 扩展
NEXT
Vim 入门 - 你需要了解的最低限度的安装 一点理论(开玩笑的) 有趣的部分! 结论