React 中的渐进式图像加载

React 中的渐进式图像加载技术,像 Gatsby 和 Next.js 这样的 React 框架也在它们的图像组件中使用这种模式。

React 中的渐进式图像加载

图片可以对网站产生很大的影响。他们的存在改善了用户体验并有助于提高参与度。但是,加载高质量的图像可能需要一些时间,并且会使这种体验更加令人沮丧,尤其是在互联网连接速度较慢的情况下。

为了解决这个问题,开发人员需要部署一种支持积极加载体验的策略。一种这样的策略是渐进式图像加载。

我们将了解渐进式图像加载、如何在 React 中部署此策略等。

  • 为什么渐进式图像加载很有用
  • React 中的渐进式图像加载技术
    • 创建图像组件
    • 将缩略图更新为实际图像
    • 实现过渡模糊
    • 使用库进行渐进式图像加载

为什么渐进式图像加载很有用

通过渐进式图像加载,开发人员可以显示低分辨率或预览图像,直到实际图像加载。

下面的 GIF 演示了使用原生 <img />

正如我们所见,尽管页面已经加载,但图像在渲染之前需要额外的一秒钟,从而导致空白。当我们的互联网连接速度非常慢时,体验会变得更糟。

通过使用渐进式加载技术,我们可以渲染图像的微小文件大小版本以减少加载时间。加载高分辨率版本后,我们可以交换图像文件。请参阅下面的 GIF 演示:

由于占位符图像几乎立即加载,此策略还可以帮助减少由网页图像引起的布局偏移问题。请注意,布局变化主要是因为浏览器不知道要为图像保留多少空间。

我们可以通过向图像添加宽度和高度属性来防止这种行为。这通知浏览器为图像保留一定数量的空间。然后,我们必须在 CSS 文件中的图像上应用 max-width: 100% 和 height: auto 以确保图像在响应式布局中的行为正确。

在本教程中,我们将学习如何通过从头开始在 React 中逐步加载图像来改善用户体验并防止布局变化。我们还将学习如何使用外部库来实现相同的结果。

React 中的渐进式图像加载技术

渐进式图像的魔力是通过创建两个图像版本来实现的:实际图像和一个较小的文件版本,通常小于 2kB。

首先加载低质量图像以快速显示并在主图像下载时按比例放大以适应主图像宽度。然后,应用模糊滤镜和适当的 CSS 过渡。

像 Gatsby 和 Next.js 这样的 React 框架也在它们的图像组件中使用这种模式。但不是用户手动创建图像的小版本,框架会自动从源图像生成它。

此外,这些框架使用高级图像处理选项,并支持在屏幕下方延迟加载图像。

创建图像组件

我们将创建一个名为 ProgressiveImg 的图像组件来封装 <img /> 元素和渐进式加载的逻辑。此组件可以用作原生 <img /> 元素的替代品

const ProgressiveImg = ({ placeholderSrc, src, ...props }) => {
  return (
    <img
      {...{ src: placeholderSrc, ...props }}
      alt={props.alt || ""}
      className="image"
    />
  );
};
export default ProgressiveImg;

在上面的代码中,组件接收实际的图像源、占位符源以及我们传递的所有其他道具。然后,我们将这些道具分配给 <img /> 元素属性。 请注意我们如何使用 ... 扩展运算符来注入组件接收的任何其他道具。例如,组件将接收所需的图像宽度和高度,我们稍后会看到。同时,如前所述,我们为 src 分配了一个占位符图像源以便快速显示。 接下来,让我们确保将上述所有道具传递给 <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 中的变量,让我们更新 ProgressiveImg 组件:

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 Hook,这样我们就有了以下内容:

useEffect(() => {
  const img = new Image();
  img.src = src;
  img.onload = () => {
    setImgSrc(src);
  };
}, [src]);

在这个 Hook 中,我们首先通过实例化 Image() 对象并将 src 属性设置为实际图像源来创建一个 img 元素。

使用图像对象上的 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

然后我们可以导入 ProgressiveImage 组件并像这样实现它:

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>
    // ...
  );
}

ProgressiveImage 组件使用渲染技术来实现渐进式图像加载。在它的子函数 prop 中,我们可以访问渲染回调中的 src 和加载参数。

使用 loading 参数,我们可以动态地将类添加到 img 元素。加载实际图像时,加载返回 true;否则,它返回 false。