使用 Gatsby 实现模糊加载效果的图像组织和准备方法

Avatar of Ajay
Ajay

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

Gatsby 在处理和管理图像方面做得非常出色。例如,它可以帮助您节省图像优化的时间,因为您无需手动优化每个图像。

通过插件和一些配置,您甚至可以使用 Gatsby 为图像设置图像预加载和一种称为**模糊加载**的技术。这有助于提供更流畅、更快速、更吸引人的用户体验。

我发现将gatsby-source-filesystemGraphQLSharp 插件gatsby-image组合起来进行组织和理解非常繁琐,尤其是在这种功能相当普遍的情况下。此外,gatsby-image 的工作方式与普通的<img>标签完全不同,并且在配置整个系统时,实现网站的一般用例可能会变得复杂。

Medium 使用模糊加载技术处理图像。

如果您还没有这样做,应该阅读gatsby-image 文档。它是 Gatsby 用于处理和放置响应式、延迟加载图像的 React 组件。此外,它还保存图像位置,防止在加载时页面跳动,您甚至可以为每个图像创建模糊加载预览。

对于响应式图像,您通常会使用<img>标签,并在srcset属性中包含一系列大小合适的图像,以及一个sizes属性,用于告知图像将用于的布局情况。

<img srcset="img-320w.jpg 320w,
              img-480w.jpg 480w,
              img-800w.jpg 800w"
      sizes="(max-width: 320px) 280px,
            (max-width: 480px) 440px,
            800px"
      src="img-800w.jpg">

您可以在Mozilla 文档中了解更多关于此工作原理的信息。这是使用 gatsby-image 的优势之一:它会在设置srcset属性的同时自动执行所有调整大小和压缩操作,这些属性位于<img />标签中。

图像目录结构

项目规模和复杂性很容易增长。即使是一个单页面网站也可能包含大量的图像资源,从图标到完整的幻灯片。将图像按某种顺序组织起来,而不是将它们全部堆积在服务器上的单个目录中,会很有帮助。这有助于我们更直观地设置处理流程并分离关注点。

在尝试组织文件时,还需要考虑的是,Gatsby 使用自定义的 webpack 配置来处理、压缩和导出项目中的所有文件。生成的输出将放置在/public文件夹中。gatsby-starter-default 使用的整体结构如下所示

/
|-- /.cache
|-- /plugins
|-- /public
|-- /src
    |-- /pages
    |-- /components
    |-- /images
    |-- html.js
|-- /static (not present by default)
|-- gatsby-config.js
|-- gatsby-node.js
|-- gatsby-ssr.js
|-- gatsby-browser.js

在此处阅读有关 Gatsby 项目结构的工作原理的更多信息 此处

让我们从可能遇到的需要组织的常见图像文件开始

例如

  • 图标
  • 徽标
  • 网站图标
  • 装饰性图像(通常是矢量或 PNG 文件)
  • 图像库(例如“关于我们”页面上的团队头像等)

我们如何分组这些资源?考虑到我们追求效率的目标以及上面提到的 Gatsby 项目结构,最佳方法是将其分成两组:一组不需要处理,可以直接导入项目;另一组需要处理和优化的图像。

您的定义可能有所不同,但该分组可能如下所示

静态,不需要处理

  • 不需要处理的图标和徽标
  • 预优化图像
  • 网站图标
  • 其他矢量文件(如装饰性艺术作品)

需要处理

  • 非矢量艺术作品(例如 PNG 和 JPG 文件)
  • 图库图像
  • 任何其他可以处理的图像,基本上是矢量以外的常见图像格式

现在,我们已经以某种顺序组织了这些内容,可以继续管理每个组。

“静态”组

Gatsby 提供了一个非常简单的处理静态组的方法:将所有文件添加到项目根目录下名为static的文件夹中。打包程序会自动将内容复制到public文件夹中,最终构建可以直接访问这些文件。

假设您有一个名为logo.svg的文件,它不需要处理。将其放在static文件夹中,并在组件文件中使用它,如下所示

import React from "react"

// Tell webpack this JS file requires this image
import logo from "../../static/logo.svg" 

function Header() {
  // This can be directly used as image src
  return <img src={logo} alt="Logo" />
}

export default Header

是的,就这么简单——就像导入组件或变量然后直接使用它一样。Gatsby 有详细的文档,介绍如何将资源直接导入文件,您可以参考以进一步了解。

特殊情况:网站图标

插件gatsby-plugin-manifest不仅会向项目添加manifest.json文件,还会为所有所需大小生成网站图标并在网站中链接它们。

通过最少的配置,我们就可以拥有网站图标,无需手动调整大小,也无需在 HTML 头部添加单独的链接。将favicon.svg(或 .png 或您使用的任何格式)放在static文件夹中,并使用gatsby-plugin-manifest的设置调整gatsby-config.js文件。

{
  resolve: `gatsby-plugin-manifest`,
  options: {
    name: `Absurd`,
    icon: `static/favicon.svg`,
  },
},

“处理”组

理想情况下,我们希望gatsby-imageimg标签一样工作,我们指定src,它会在后台执行所有处理。不幸的是,它并不那么简单。Gatsby 要求您配置gatsby-source-filesystem以处理文件,然后使用 GraphQL 查询并使用 Gatsby Sharp 插件(例如gatsby-transformer-sharpgatsby-plugin-sharp)和gatsby-image对其进行处理。结果是一个响应式、延迟加载的图像。

我不会带您逐步了解如何在 Gatsby 中设置图像处理(这已经在Gatsby 文档中得到了很好的说明),而是将向您展示针对一些常见用例优化此过程的几种方法。我假设您具备 Gatsby 图像处理工作原理的基本知识——但如果没有,我强烈建议您先阅读文档。

用例:图像库

让我们以“关于我们”页面上的个人资料图像的常见情况为例。排列方式基本上是一个包含标题、描述和图像的数据数组,以网格或集合的形式显示在特定部分。

数据数组将类似于

const TEAM = [
  {
    name: 'Josh Peck',
    image: 'josh.jpg',
    role: 'Founder',
  },
  {
    name: 'Lisa Haydon',
    image: 'lisa.jpg',
    role: 'Art Director',
  },
  {
    name: 'Ashlyn Harris',
    image: 'ashlyn.jpg',
    role: 'Frontend Engineer',
  }
];

现在,让我们将所有图像(josh.jpglisa.jpg等)放在src/images/team中。您可以根据组别在images中创建文件夹。由于我们正在处理“关于我们”页面上的团队成员,因此我们使用了images/team。下一步是查询这些图像并将其与数据链接起来。

为了使这些文件在 Gatsby 系统中可用以进行处理,我们使用 gatsby-source-filesystem。此特定文件夹的gatsby-config.js中的配置如下所示

{
  resolve: `gatsby-source-filesystem`,
  options: {
    name: `team`,
    path: `${__dirname}/src/images/team`,
  },
  `gatsby-transformer-sharp`,
  `gatsby-plugin-sharp`,
},

要查询来自此特定文件夹的文件数组,我们可以使用sourceInstanceName。它采用gatsby-config.js中指定的名称的值。

{
  allFile(filter: { sourceInstanceName: { eq: "team" } }) {
    edges {
      node {
        relativePath
        childImageSharp {
          fluid(maxWidth: 300, maxHeight: 400) {
            ...GatsbyImageSharpFluid
          }
        }
      }
    }
  }
}

这将返回一个数组

// Sharp-processed image data is removed for readability
{
  "data": {
    "allFile": {
      "edges": [
        {
          "node": {
            "relativePath": "josh.jpg"
          }
        },
        {
          "node": {
            "relativePath": "ashlyn.jpg"
          }
        },
        {
          "node": {
            "relativePath": "lisa.jpg"
          }
        }
      ]
    }
  }

如您所见,我们使用relativePath将我们需要关联的图像与数据数组中的项目关联起来。一些简单的 JavaScript 可以在这里提供帮助

// Img is gatsby-image
// TEAM is the data array

TEAM.map(({ name, image, role }) => {
  // Finds associated image from the array of images
  const img = data.allFile.edges.find(
    ({ node }) => node.relativePath === image
  ).node;

  return (
    <div>
      <Img fluid={img.childImageSharp.fluid} alt={name} />
      <Title>{name}</Title>
      <Subtitle>{role}</Subtitle>
    </div>
  );
})

这是我们最接近使用类似于<img>标签的src的方式。

用例:艺术作品

尽管艺术作品可以使用相同类型的文件创建,但这些文件通常分散在不同部分(例如页面和组件)中,并且每个文件通常具有不同的尺寸。

很明显,像之前那样查询整个数组是行不通的。但是,我们仍然可以将所有图像组织到一个文件夹中。这意味着我们仍然可以使用sourceInstanceName来指定我们要从哪个文件夹中查询图像。

类似于我们之前的用例,让我们创建一个名为src/images/art的文件夹并配置gatsby-source-filesystem。在查询时,我们不会获取整个数组,而是根据我们的需求查询特定尺寸和规格的图像。

art_team: file(
    sourceInstanceName: { eq: "art" }
    name: { eq: "team_work" }
  ) {
    childImageSharp {
    fluid(maxWidth: 1600) {
      ...GatsbyImageSharpFluid
    }
  }
}

这可以直接在组件中使用。

<Img fluid={data.art_team.childImageSharp.fluid} />

此外,对于需要从该组中获取图像的每个组件或部分,都可以重复此操作。

特殊情况:内联 SVG

Gatsby 会自动将较小的图像编码为 base64 格式并将其数据内联,从而减少请求次数以提高性能。这通常很好,但实际上可能会对 SVG 文件造成损害。相反,我们可以手动处理 SVG 以获得相同的性能优势,或者在我们需要使内容更具交互性时,加入动画。

我发现gatsby-plugin-svgr 是这里最方便的解决方案。它允许我们将所有 SVG 文件导入为 React 组件。

import { ReactComponent as GithubIcon } from './github.svg';

由于我们在技术上处理的是 SVG 文件而不是栅格图像,因此将 SVG 文件从static文件夹中移出并将其放在使用它的组件的文件夹中是有意义的。

结论

在几个项目中使用 Gatsby 后,我克服了在处理图像以获得良好的模糊放大效果时遇到的几个障碍。我认为它们可能对您有用,特别是对于我们所看到的常见用例。

这里使用到的所有约定都来自我在 GitHub 上设置的gatsby-absurd 启动项目。以下是结果。

如果您想查看在项目中使用的示例,最好查看一下。查看Team.js 以了解如何从同一组中查询多个图像。其他部分,例如About.jsHeader.js,演示了如何查询设计图形(跨不同部分共享的图像组)。Footer.jsNavbar.js 提供了处理图标的示例。