Руководство для начинающих по использованию Gatsby.js и GraphQL для оптимизации изображений.

Как и многие разработчики, моим первым полностью функционирующим сайтом был блог. Я создал его как настраиваемую тему WordPress, и у меня были грандиозные планы относительно домашней страницы, заполненной высококачественными изображениями статей.

Когда я впервые запустил сайт в прямом эфире, я ввел URL-адрес и… ждал. Это был серьезный анти-кульминационный момент. Прошло слишком много секунд, пока образы медленно оживали.

До этого момента я не производил значительных оптимизаций изображений. Это был важный урок для относительно нового разработчика, и я начал изучать, как сделать как можно больше оптимизаций. Но оптимизация размеров изображения, настройка разных размеров и разрешений каждого изображения для разных дисплеев и настройка отложенной загрузки с хорошей анимацией «постепенного появления» - это большая работа. Попытки решить их вручную были хороши для обучения, но определенно не то, что я хотел делать снова и снова.

К счастью, есть способ получше. Теперь, как разработчик React, я столкнулся с множеством различных систем и модулей обработки изображений, которые упрощают оптимизацию изображений. Но - пока - ничто из встреченных мной не может сравниться с Gatsby.js.

Используя несколько компонентов Gatsby, вы можете легко оптимизировать доставку изображений - в комплекте с «размытой» анимацией или отслеживаемыми заполнителями SVG - плюс дополнительные оптимизации, такие как использование форматов изображений WebP для браузеров, которые их поддерживают. Они загружаются быстро и выглядят действительно гладко при идеальном разрешении.

Представляем Gatsby Image Optimization

Gatsby.js упрощает оптимизацию изображений, за исключением одного: если вы никогда раньше не использовали GraphQL, этот процесс тоже может занять некоторое время. Мне потребовалось несколько усилий, чтобы разобраться с изображением Gatsby, в основном из-за того, что я не прыгнул в GraphQL.

Я также чувствовал, что многие учебные пособия (включая официальные) не дают возможности объяснить, как работать с более чем одним изображением. Официальный стартовый пакет Gatsby содержит компонент изображения, и он будет работать нормально, если на вашем сайте будет только несколько изображений. Но что, если бы их было десятки или сотни?

На это и собирается ответить эта статья. В нем мы рассмотрим пошаговый подход к объединению возможностей Gatsby и GraphQL для оптимизации изображений. Мы начнем с рендеринга трех изображений, а в последнем разделе я расскажу о нескольких способах увеличения.

Пошаговое руководство

Шаг 1. Установите зависимости

У Гэтсби есть два основных компонента изображения: gatsby-image и gatsby-background-image.

Чтобы использовать любой из них, вам потребуется несколько дополнительных компонентов для вашего проекта Gatsby. (Если вы новичок в Гэтсби, вы можете узнать как начать проект здесь). После того, как ваш проект Gatsby настроен, вы можете установить все необходимые плагины, связанные с изображениями, через npm, набрав:

npm i gatsby-image gatsby-background-image gatsby-source-filesystem gatsby-plugin-sharp gatsby-transformer-sharp -s

Может показаться, что это много, но каждый плагин в большей или меньшей степени выполняет одну задачу:

  • gatsby-image используется для отображения изображений
  • gatsby-background-image используется для отображения фоновых изображений
  • gatsby-source-filesystem позволяет запрашивать файлы в файловой системе вашего сайта с помощью GraphQL.
  • gatsby-transformer-sharp - это плагин, который позволяет создавать множественные изображения нужных размеров и разрешений с помощью запросов.
  • и gatsby-plugin-sharp соединяет плагины Sharp и Gatsby вместе

Шаг 2. Настройте Gatsby

После установки вы должны убедиться, что некоторые из вышеперечисленных подключаемых модулей присутствуют в вашем gatsby-config.js файле в корневом каталоге вашего веб-приложения.

В приведенном ниже примере я определил два каталога, images и pages, в которых я хотел бы иметь возможность запрашивать свою файловую систему. В этой статье мы сосредоточимся только на images, но также часто запрашивают и ваш pages каталог!

module.exports = {
  plugins: [
    `gatsby-transformer-sharp`,
    `gatsby-plugin-sharp`,
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/images`,
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `pages`,
        path: `${__dirname}/src/pages`,
      },
    },
  ],
}

Если вы привыкли устанавливать и импортировать пакеты npm, это может показаться довольно простым делом. На этом этапе все становится немного необычнее.

Шаг 3A: Тестовые запросы в GraphQL

Теперь мы собираемся получить доступ к интерфейсу GraphiQL. По умолчанию приложение Gatsby запускается localhost:8000. Мы можем получить доступ к интерфейсу GraphiQL, добавив /___graphql в конец домена (это 3 символа подчеркивания подряд).

Здесь мы можем опробовать различные запросы к нашим данным, прежде чем вносить их в наш код. Это сэкономит нам время на отладку позже, потому что мы знаем, что запросы собирают те данные, которые нам нужны.

Во-первых, давайте проверим, что наш gatsby-config.js файл работает правильно. Введите следующий код в интерфейс GraphiQL и нажмите значок воспроизведения (или CTRL/CMD + ENTER):

{
  allDirectory {
    edges {
      node {
        name
      }
    }
  }
}

Если вы видите что-то похожее на изображение выше, значит, оно работает. Теперь давайте запросим содержимое нашей папки изображений, набрав:

{
  allFile(filter:{ sourceInstanceName:{eq: "images"} }){
    edges{
      node{
        relativePath
        childImageSharp {
          id
        }
      }
    }
  }
}

Если свойство childImageSharp возвращает идентификатор, это означает, что мы можем использовать для него оптимизацию изображения Gatsby. Это вернет null для файлов, таких как SVG, потому что они не могут быть дополнительно оптимизированы, но он должен дать вам строку для каждого jpg и png.

Шаг 3B: подготовьте наши конкретные запросы изображений

А теперь давайте возьмем несколько конкретных изображений. При выполнении запросов к изображению вам необходимо сообщить Гэтсби, является ли это изображение fixed или fluid. fixed изображения имеют известные размеры, и для их оптимизации требуется меньше процессов. fluid изображения имеют размеры, которые меняются в зависимости от размера области просмотра и других контекстных факторов.

Я создаю портфолио, и у меня есть изображения для каждой из моих услуг. Допустим, мы хотим получить три изображения с именами webdev.jpg, design.jpg и writing.jpg, и знаем, что их размеры равны fluid.

{
 
  webdev:file(relativePath:{eq:"webdev.jpg"}) {
    childImageSharp {
      fluid(maxWidth: 1600) {
        base64
      }
    }
  }
  
  design:file(relativePath:{eq:"design.jpg"}) {
    childImageSharp {
      fluid(maxWidth: 1600) {
        base64
      }
    }
  }
  
  writing:file(relativePath:{eq:"writing.jpg"}) {
    childImageSharp {
      fluid(maxWidth: 1600) {
        base64
      }
    }
  }
  
}

Обратите внимание, что термины перед каждым двоеточием могут быть любыми. Здесь имеет смысл придерживаться имени файла. Мы также устанавливаем свойство max-width равным 1600 пикселей, поэтому Гэтсби знает, что ему не нужно подготавливать версии каждого изображения большего размера.

base64 - это свойство, которое содержит крошечную размытую версию нашего изображения, которая загружается почти сразу, а затем плавно заменяется версией с высоким разрешением. Если наш запрос возвращает значение base64, значит, все работает. Мы готовы включить этот запрос в наш код!

Шаг 4: Импорт компонентов и рендеринг

Перейдите к любому компоненту, в котором вы хотите отображать изображения. Во-первых, вам нужно импортировать компоненты StaticQuery и graphql из "gatsby" в верхней части файла, а также Img или BackgroundImage, например:

import { StaticQuery, graphql } from "gatsby"
import Img from "gatsby-image"
import BackgroundImage from "gatsby-background-image"

Наш компонент React должен возвращать тег <StaticQuery>, который является свойством query и свойством render.

<StaticQuery query={
  graphql`{
    # our GraphQL queries go here
  `} 
  render={(data) => (
    <>
      {/* our JSX goes here */}
    </>
  )}
/>

Мы можем вставить наши запросы изображений, указанные выше, в свойство query, но на этот раз мы заменим base64 фрагментом, который мы хотим визуализировать. В этом случае мы будем использовать ...GatsbyImageSharpFluid.

Но, допустим, позже мы решили, что нам нужен эффект отслеживаемого SVG, и что мы хотим использовать формат WebP, где это возможно. Мы можем просто поменять наш фрагмент на ...GatsbyImageSharpFluid_withWebp_tracedSVG.

Теперь наш код должен выглядеть так:

<StaticQuery query={
  graphql`{
    webdev:file(relativePath:{eq:"webdev.jpg"}) {
    childImageSharp {
      fluid(maxWidth: 1600) {
        ...GatsbyImageSharpFluid
      }
    }
  }
  
  design:file(relativePath:{eq:"design.jpg"}) {
    childImageSharp {
      fluid(maxWidth: 1600) {
        ...GatsbyImageSharpFluid
      }
    }
  }
  
  writing:file(relativePath:{eq:"writing.jpg"}) {
    childImageSharp {
      fluid(maxWidth: 1600) {
        ...GatsbyImageSharpFluid
      }
    }
  }
  `} 
  render={(data) => (
    <>
      {/* our JSX goes here */}
    </>
  )}
/>

Наконец, нам просто нужно включить изображение в наш JSX.

Компонент Img принимает свойство fluid (где вы указываете ссылку на данные запроса) и свойство alt.

<Img 
  fluid={data.webdev.childImageSharp.fluid}
  alt=""
/>

Компонент BackgroundImage принимает свойство tag (если его оставить пустым, он отображает div), свойство fluid и свойство backgroundColor.

<BackgroundImage
  tag="section"
  fluid={data.webdev.childImageSharp.fluid}
  backgroundColor={`#000`}
>
  {/* the child elements that go above the background */}
</BackgroundImage>

Собираем все вместе

Вот полный компонент Gatsby, который берет три изображения для нашей images папки и отображает их:

Стратегии масштабирования

Итак, как мы можем заставить эту работу работать, когда нам нужно обрабатывать большее количество изображений? Вот несколько идей для начала.

Цикл по папке

Допустим, у нас есть список значков, которые мы хотим отобразить. Вместо того, чтобы запрашивать их все по отдельности, мы могли бы поместить их в отдельный каталог и использовать цикл для перебора результатов запроса. Например, мы могли бы запросить весь каталог «icons»:

{
  icons:allFile(filter:{ relativeDirectory:{eq: "icons"} }){
    edges{
      node{
        name
        relativePath
        childImageSharp {
          id
        }
      }
    }
  }
}

Затем, если мы зарегистрируем data.icons.edges в консоль, мы сможем увидеть массив элементов, по которым можно выполнить итерацию. Вот пример того, как это может выглядеть.

data.icons.edges.map(item => (
  <Img 
    fluid={item.node.childImageSharp.fluid} 
    alt={item.node.name} 
  />
))

Передача динамических данных

Один из важных методов - это добавление динамических переменных в наши запросы. В GraphQL для этого есть специальный синтаксис.

Для этого дадим нашему запросу имя findFile с помощью ключевого слова query. Затем в скобках мы можем назвать любое количество новых переменных.

В GraphQL всем переменным должен предшествовать $. После имени переменной мы используем двоеточие, а затем указываем тип: здесь String. Наконец, мы можем использовать = для передачи резервного значения по умолчанию, и это позволит нам протестировать запрос в GraphiQL.

query findFile($relativePath: String = "webdev.jpg") {
  file(relativePath: {eq: $relativePath}) {
    id
    relativePath
    publicURL
  }
}

К этим запросам можно добавить дополнительную логику с помощью директив @include(if: Boolean) и @skip(if: Boolean).

Переменные GraphQL особенно полезны, если мы хотим, чтобы наши пользователи могли динамически фильтровать данные. Но они также пригодятся по любой причине, по которой мы можем захотеть отделить определенные данные от нашего фактического запроса, например, если нам нужно сначала каким-то образом изменить их.

Создание пользовательских фрагментов

Помните ...GatsbyImageSharpFluid выше? Это фрагмент, который по сути является сокращением для многоразового набора полей запроса. Мы также можем определять наши собственные фрагменты.

Даже если мы вызываем только три поля запроса - id, relativePath и publicURL - это может добавить к многим строкам кода дополнительный код, если мы будем использовать их неоднократно. Вместо этого мы могли бы определить настраиваемый фрагмент, позволяющий повторно использовать поля запроса. Вместо того, чтобы повторять описание того, как это сделать, в официальной документации, я рекомендую вам проверить это.

Заключение

В целом, я надеюсь, что эта статья открыла вам глаза на мощные инструменты оптимизации изображений, которые поставляются с Gatsby.js, и дала вам несколько идей о том, как применять их в крупномасштабных проектах.

Чтобы полностью понять и использовать эти мощные инструменты по максимуму, вам нужно хорошо разбираться как в React, так и в GraphQL. Когда я впервые начал использовать Gatsby, я пропустил GraphQL, не понимая, что его понимание - по крайней мере, на базовом уровне - необходимо для максимально эффективного использования возможностей Gatsby по оптимизации изображений.