使用 React Hooks 创建延迟加载图像组件
延迟加载图片(例如 Medium 中的图片或gatsby-image 🧡 创建的图片)有时可以为页面增添一抹额外的风格。要实现这种效果,需要:A)一个用于预览的缩略图,最好以 data URL 的形式内联;B)设置图片的宽高比,用于创建占位符以防止回流。在本文中,我将分享如何使用 React Hooks 创建一个延迟加载的图片组件。
嗨,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>
- 相对定位的包装器
div
, - 用于保持宽高比的固有占位
div
符。它具有 padding-bottom 属性,其值为百分比值(相对于包含块的宽度)。例如,对于 16:9 的图像,其百分比计算为 9/16 * 100% = 56.25%。 - 绝对定位
img
,用于图片的微小版本,也称为 LQIP(低质量图片占位符) ,拉伸以覆盖包装器。数据 URL 通常用作 src 来保存 HTTP 请求, - 绝对定位的
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 源了。✅)
文章来源:https://dev.to/hangindev/create-a-lazy-loading-image-component-with-react-hooks-4p19React 峰会将于 10 月 15 日至 16 日回归。届时将有Kent C. Dodds、Max Stoiber、Sara Vieira和Sharif Shameem等嘉宾发表演讲。
9 月 20 日前免费注册:https://ti.to/gitnation/react-summit?source =REFURCR-1 。