实战:使用 React 实现渐进式加载图片
图片对网站有很大的影响。它们的存在改善了用户体验,提高了用户粘性。然而,加载高质量的图片需要时间,而且会让这种体验更令人沮丧,尤其是在网速较慢的情况下。
为了解决这个问题,开发人员需要部署支持积极加载体验的策略。其中一个策略是渐进式图像加载。
在本文中,我们将学习渐进式图像加载,如何在React中实现这个策略。我将从以下几个步骤介绍:
- 为什么渐进式图像加载是有用的
- React中的渐进式图像加载技术
- 创建一个图像组件
- 将缩略图更新为实际图像
- 实现过渡模糊
- 使用库逐步加载图像
为什么渐进式图像加载是有用的
使用渐进式图像加载,开发人员可以显示低分辨率的图像或预览图像,直到实际的图像加载。这通过提供图像随时出现的感知来改善用户体验。
下面的GIF演示了如何使用本地元素<img />
来渲染图像。
正如我们所看到的,尽管页面已经加载,但图像在呈现之前需要多花一秒钟的时间,从而导致空白。当我们的网络连接速度非常慢时,这种体验就会恶化。
通过使用渐进式加载技术,我们可以渲染图像的小文件大小版本,以减少加载时间。一旦加载了高分辨率版本,我们就可以交换图像文件了。请看下面的GIF演示: 由于占位符图像几乎是立即加载的,这种策略也可以帮助减少由网页图像引起的布局变化问题。请注意,出现布局变化主要是因为浏览器不知道要为图像保留多少空间。
我们可以通过添加图像的宽度和高度属性来防止这种行为。这将通知浏览器为图像预留一定数量的空间。然后我们必须对CSS文件中的图像应用max-width: 100%和height: auto
,以确保图像在响应式布局中的正确行为。
在本文中,我们将学习如何改进用户体验,并通过在React中从无到有地加载图像来防止布局变化。我们还将学习如何使用外部库来实现相同的结果。
React 中的渐进式图像加载技术
渐进式图像的魔力是通过创建两个图像版本实现的:即实际图像和较小的文件版本(通常小于2kB)。
低质量的图像首先被加载以快速显示,然后在主图像下载时被放大以适应主图像的宽度。然后,一个模糊过滤器和适当的CSS过渡应用。
像Gatsby
和Next.js
这样的React框架也在它们的图像组件中使用了这种模式。但是,框架不是让用户手工创建一个小版本的图像,而是从源图像自动生成它。
此外,这些框架使用高级的图像处理选项,并允许延迟加载屏幕下方的图像。
在我们的例子中,焦点是使用React实现渐进图像加载。让我们开始实现它。
创建一个图像组件
我们将创建一个名为ProgressiveImg
的图像组件,以封装<img />
元素和用于渐进加载的逻辑
。然后可以使用该组件替换本地<img />
元素。
const ProgressiveImg = ({ placeholderSrc, src, ...props }) => {
return (
<img
{...{ src: placeholderSrc, ...props }}
alt={props.alt || ""}
className="image"
/>
);
};
export default ProgressiveImg;
在上面的代码中,**组件接收实际的图像源src
、它的占位符源placeholderSrc
和我们传递的其他所有props
。**然后,我们将这些props
分配给<img />
元素属性。
注意我们是如何使用…
扩展操作符来注入组件接收到的任何其他props
的。例如,我们将在稍后看到,组件将接收所需的图像宽度和高度。与此同时,我们为src
分配了一个占位符图像源,以便快速显示。
接下来,让我们确保将上述所有props
传递给<ProgressiveImg />
,就像这样:
import ProgressiveImg from "./components/ProgressiveImg";
import image from "./images/large_.jpg";
import placeholderSrc from "./images/tiny_.jpg";
export default function App() {
return (
// ...
<ProgressiveImg
src={image}
placeholderSrc={placeholderSrc}
width="700"
height="465"
/>
// ...
);
}
正如在代码中所看到的,我们传递了图像及其小尺寸版本,并将其调整为小于2kB
。我们还必须沿着图像的宽度和高度传递,以防止布局偏移。
如果图像大小大于指定的值,请确保保持长宽比。这样,前端应该看起来像这样:
将缩略图更新为实际图像
为了更新img
的src
并呈现实际的图像,我们必须通过useState Hook
将默认的图像源存储在一个状态变量中。然后,我们可以在实际图片加载后**更新useEffect Hook中的变量。**如下所示:
import { useState, useEffect } from "react";
const ProgressiveImg = ({ placeholderSrc, src, ...props }) => {
const [imgSrc, setImgSrc] = useState(placeholderSrc || src);
useEffect(() => {
// update the image
}, []);
return (
<img
{...{ src: imgSrc, ...props }}
alt={props.alt || ""}
className="image"
/>
);
};
export default ProgressiveImg;
注意,img
的src
属性现在被分配了一个状态变量的值。默认情况下,如果我们有占位符,这个值会被设置为它。否则,它将被分配主图像。
接下来,让我们更新useEffect
钩子:
useEffect(() => {
const img = new Image();
img.src = src;
img.onload = () => {
setImgSrc(src);
};
}, [src]);
在这个Hook中,我们首先创建一个img
元素,方法是实例化一个Image()
对象并将src
属性设置为实际的图像源。
使用图像对象的onload
事件处理程序,我们可以检测实际的图像何时在后台完全加载。然后,我们将图像src更新为实际的图像。
实现过渡模糊
让我们添加一个CSS过渡模糊平滑的效果。在ProgressiveImg
组件中,在return
语句的上方添加以下代码:
const customClass =
placeholderSrc && imgSrc === placeholderSrc ? "loading" : "loaded";
我们将根据加载状态动态地向图像添加类名。
因此,更新<img />
以包含自定义类名:
return (
<img
// ...
className={`image ${customClass}`}
/>
);
如果实际图像仍在加载中,则向图像添加一个loading
类。否则,我们添加一个loaded
类。我们更新CSS
,使其包含样式规则:
.loading {
filter: blur(10px);
clip-path: inset(0);
}
.loaded {
filter: blur(0px);
transition: filter 0.5s linear;
}
使用第三方库实现
我们还可以通过使用一个名为react-progressive-graceful-image
的库来实现渐进式加载图像。要使用它,我们必须安装它:
npm i react-progressive-graceful-image
然后我们可以导入progresveimage
组件,并像这样实现它:
import ProgressiveImage from "react-progressive-graceful-image";
import image from "./images/large_.jpg";
import placeholderSrc from "./images/tiny.jpg";
export default function App() {
return (
// ...
<ProgressiveImage src={image} placeholder={placeholderSrc}>
{(src, loading) => (
<img
className={`image${loading ? " loading" : " loaded"}`}
src={src}
alt="sea beach"
width="700"
height="465"
/>
)}
</ProgressiveImage>
// ...
);
}
progresveimage
组件使用props
来实现渐进式图像加载。在它的子函数prop
中,我们可以在渲染回调函数中访问src
和loading
参数。
通过loading
参数,我们可以动态地向img
元素添加类。当实际图像加载时,loading
返回true;否则,返回false。
结尾
通过实现渐进式图像加载技术,我们可以极大地改善React项目中的用户体验。
在本文中,我们介绍了如何在React中加载有外部库和没有外部库的图像。我希望你已经学到了很多,并且喜欢这篇文章。