Многоразовый компонент Gatsby-Image с динамическими источниками изображений

Я подумываю использовать Gatsby-Image для своего следующего проекта и немного поэкспериментировал с ним.

Я заставил его работать над своим тестовым проектом, но затем я придумал вариант использования, в котором я хотел бы использовать от Гэтсби так же, как обычный тег <img src”image.png”>. Поэтому мой вопрос заключается в том, как я могу сделать компонент Gatsby многоразовым?

import React from "react"
import { StaticQuery, graphql } from "gatsby"
import Img from "gatsby-image"
function renderImage({ file }) {
  console.info({ file })
  return <Img fluid = {file.childImageSharp.fluid} />
}

// Stateless Image component which i guess will recieve src value as a prop?
// Returns a StaticQuery component with query prop and render prop. Query prop has the graphql query to recieve the images and render prop returns a renderImage function which in return, returns a Img component från Gatsby with set attributes.
const Image = () => (
  <StaticQuery
    query = {graphql`
      query {
        file(relativePath: { eq: "gatsby-astronaut.png" }) {
          childImageSharp {
            fluid(maxWidth: 300) {
              ...GatsbyImageSharpFluid
            }
          }
        }
      }
    `}
    // render = {data => <Img fluid = {data.placeholderImage.childImageSharp.fluid} />}
    render = {renderImage}
  />
)
export default Image

Моим оптимальным вариантом использования было бы сделать динамический запрос к моему относительному пути, который определен в моем файле Gatsby.config, а затем объединить реквизит src в каждом Gatsby и сопоставить его со всеми моими изображениями в моем файле ресурсов, а затем отобразить его. Кто-нибудь из вас знает, возможно ли это вообще с ?

Я читал в документах, что Static Query не может принимать переменные - только страницы. Но я не хочу, чтобы мои изображения были связаны со страницей — я хочу использовать этот компонент везде, где захочу — как обычный тег img.

Надеюсь, я ясно выразился. Пожалуйста, спросите, есть ли у вас какие-либо вопросы.

Вот пример: https://codesandbox.io/s/py5n24wk27

Заранее спасибо, Эрик

Поскольку gatsby-image требует предварительной обработки от самого gatsby, невозможно сделать его по-настоящему динамичным во время выполнения, как с тегом img. Вы должны использовать StaticQuery или получить данные в обычном запросе страницы.

Tholle 12.03.2019 14:42

Я удалил свой ответ, потому что только что попробовал и могу подтвердить, что в настоящее время это невозможно. Есть техническое ограничение, Gatsby фактически извлекает и анализирует запросы заранее и не оценивает динамический код при этом. Похоже на довольно конкретное техническое соображение. Источник: github.com/gatsbyjs/rfcs/pull/3

Richard Vanbergen 12.03.2019 15:24

Хм, хорошо, я понимаю. Поиск продолжается, я думаю! Спасибо

erikos93 12.03.2019 16:00
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
32
3
12 729
4

Ответы 4

Я тоже искал этот ответ. Надеюсь, это ответит на ваш вопрос:

Окончательный код:

import React from 'react';
import { StaticQuery, graphql } from 'gatsby';
import Img from 'gatsby-image';

// Note: You can change "images" to whatever you'd like.

const Image = props => (
  <StaticQuery
    query = {graphql`
      query {
        images: allFile {
          edges {
            node {
              relativePath
              name
              childImageSharp {
                fluid(maxWidth: 600) {
                  ...GatsbyImageSharpFluid
                }
              }
            }
          }
        }
      }
    `}
    render = {data => {
      const image = data.images.edges.find(n => {
        return n.node.relativePath.includes(props.filename);
      });
      if (!image) {
        return null;
      }

      //const imageSizes = image.node.childImageSharp.sizes; sizes = {imageSizes}
      return <Img alt = {props.alt} fluid = {image.node.childImageSharp.fluid} />;
    }}
  />
);

export default Image;

Использование изображения:

import Image from '../components/Image';
<div style = {{ maxWidth: `300px` }}>
    <Image alt = "Gatsby in Space" filename = "gatsby-astronaut.png" />
</div>

Объяснение

Поскольку StaticQuery не поддерживает интерполяцию строк в своем литерале шаблона, мы не можем передать ему какие-либо реквизиты. Вместо этого мы попытаемся обработать проверку реквизитов в части рендеринга StaticQuery.

Предостережения

Я не уверен на 100%, влияет ли это на время компиляции, поскольку мы сканируем все изображения. Если это так, пожалуйста, дайте мне знать!

Обновление: если у вас много изображений, размер пакета может стать довольно большим, так как это решение сканирует ВСЕ изображения.

Дальнейшая настройка

Вы можете настроить код так, чтобы отображалось изображение-заполнитель, если реквизиты не переданы.

Альтернативы

Тем не менее, есть другой способ справиться с этим, но с немного большей работой/кодом.

Источники

  • Я изменил код из эта статья. (Обратите внимание, что в статье использовался устаревший код.)

Команда Гэтсби действительно упустила мяч. Кто бы мог подумать, что веб-разработчики не захотят создавать отдельный запрос и компонент для каждого отдельного изображения. /с

Jonathan Dumaine 07.11.2019 00:51

Это приятно. Обратите внимание, что вы платите за пакет javascript со всеми этими изображениями. Это не проблема, если они все используются. Однако, если вы добавите 2-3 компонента изображения, чтобы получить разные размеры (разные варианты), вы оплатите всю эту стоимость в комплекте. -- Так что это будет работать хорошо, если вы можете сохранить одинаковые параметры для всех изображений И использовать почти все изображения в запросе graphql.

odinho - Velmont 22.11.2019 16:27

Я использую allFile с относительным каталогом. Поэтому я могу отображать только изображения в определенной подпапке. allFile(filter: {relativeDirectory: {eq: "team"}}) {...}

freesh 19.04.2020 14:26
fluid также устарела.
KZee 17.04.2021 09:17

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

В подходе @RodrigoLeon это приведет к ДРАМАТИЧЕСКОМУ увеличению размера пакета. Особенно, если, скажем, у вас более 50 изображений. Потому что каждый раз, когда вы используете это и перебираете все изображения, вы создаете ссылки на них в файле компонента. Так что я бы не рекомендовал делать это таким образом.

Сайт, который я создаю, представляет собой платформу электронной коммерции с тысячами изображений (для всех продуктов). Это создало серьезную проблему с использованием gatsby, который запрашивает изображения. Долгое время у меня был компонент, который запрашивал все изображения и сопоставлял их с соответствующим продуктом. (Как предложено в этом ) Это очень неэффективно, выдает предупреждения о продолжительности запроса.

Альтернативой этому является присоединение файла изображения к продукту на уровне данных, а не при попытке рендеринга.

src/gatsby-api/create-resolvers/index.js

const resolvers = {
    AWSAppSync_Product: {
        imageFile: {
            type: 'File',
            resolve: async (source, args, context, info) => {
                const node = await context.nodeModel.runQuery({
                    query: {
                        filter: {
                            Key: { eq: source.image1 }
                        }
                    },
                    type: 'S3Object',
                    firstOnly: true
                });

                if (node && node.imageFile) return node.imageFile;
            }
        },
    },
}

module.exports = {
    resolvers
}

gatsby-node.js

exports.createResolvers = async ({ createResolvers }) => {
    createResolvers(resolvers)
}

источник/компоненты/изображение/index.js

import React from 'react'
import Img from 'gatsby-image'

export const Image = props => {
  if (props.imageFile && props.imageFile.childImageSharp && props.imageFile.childImageSharp.fluid) {
    return <Img className = {props.imgClassName} alt = {props.alt} fluid = {props.imageFile.childImageSharp.fluid} />;
  }
};

Затем используйте его как:

<Image
  imageFile = {product.imageFile}
  alt = ""
/>

AWSAppSync_Product — это тип узла, к которому я прикрепляю свой файл. (который можно найти на игровой площадке graphql на локальном хосте). Разрешение будет соответствовать Key из S3Object с image1 (строкой) на продукте. Это позволяет мне напрямую использовать изображения продуктов без необходимости выполнять запрос внутри компонента изображения.

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

Вы в основном сделали то, что Острый плагин Гэтсби уже делает, но вместо того, чтобы получать изображения в типе файла, вы получаете его в своем собственном типе. Это законное решение, которое следует учитывать, если данные известны при компиляции (в вашем случае Gatsby знает, какие продукты вы запрашиваете, поэтому подготовит только все используемые изображения). Это ни в коем случае не решение, если вы хотите использовать Gatsby Image для настоящих динамических данных. Ваш ответ — хорошее напоминание, но не решение проблемы.

Martin Braun 26.01.2021 23:34

Да, это метод, используемый во всей экосистеме Гэтсби, поскольку всегда наиболее эффективно прикреплять данные перед сборкой (для повышения производительности). Очевидно, что это не работает для внешних запросов, изображения и данные должны быть известны во время сборки. Не могли бы вы объяснить, почему это не решение проблемы? Редко бывает, что данные изображения не известны во время сборки. Вы можете избежать компонента, который извлекает данные, запросив данные раньше.

LpmRaven 27.01.2021 04:51

Что ж, здорово, что мы согласны с первой частью. Однако я не говорил, что ваше решение не является решением проблемы. Я сказал, что это не решение, когда вы хотите обрабатывать «настоящие динамические данные». Так что, если вы строги и хотите использовать компонент Gatsby <Img> «как обычный тег img» (как указано в вопросе), это не сработает. Это все еще неплохо для людей, которым не нужно показывать настоящие динамические изображения, но как только кто-то реализует, например, вызов AJAX «загрузить больше продуктов», который включает показ изображений добавленных продуктов с использованием Gatsby Image, он не может работать.

Martin Braun 28.01.2021 01:41

Ах да, это полностью обходит решение для внешних компонентов и не поддерживает вызов данных на стороне клиента; все данные потребуются во время сборки. Спасибо за разъяснения.

LpmRaven 31.01.2021 16:09

Если вы используете Wordpress с WP GraphQL и хотите динамически загружать некоторые сообщения, вы столкнетесь с той же проблемой. Вы не сможете использовать замечательные функции предварительной обработки, такие как снижение качества и использование грубых заполнителей base64. Как уже упоминалось, решение @RodrigoLeon будет работать, но вы столкнетесь с огромной нагрузкой, если ваш сайт в конечном итоге разрастется за счет изображений.

Поскольку на моем сайте будет много постов и будет динамическая загрузка таких постов, мне пришлось придумать решение, которое было бы, по крайней мере, приемлемым. В итоге я генерирую childImageSharp (и предоставляю общий заполнитель base64) для динамической части моего веб-сайта, поэтому я всегда могу передать childImageSharp компоненту <Img> Gatsby.

Вот пример гибкого типа изображения для избранных изображений в сообщениях:

  • Обязательно включите это в свой GQL:
featuredImage {
  node {
    sourceUrl
    mediaDetails {
      file
      width
      height
      sizes {
        file
        name
        width
        sourceUrl
      }
    }
  }
}

После загрузки ваших сообщений отправьте каждый узел ваших узлов File (featuredImage) через эту функцию:

/**
 * Attaches a sharped image to the node for Gatsby Image.
 * @param image Dynamic image node to expand.
 * @param maxWidth Real existing width of file to use as default size.
 */
function attachChildImageSharp(
  image,
  maxWidth
) {
  const mediaDetails: IWpMediaDetails = image.mediaDetails;
  if (mediaDetails) {
    maxWidth = maxWidth || mediaDetails.width;
    image.localFile = image.localFile || {};
    image.localFile.childImageSharp = image.localFile.childImageSharp || {};
    const childImageSharp = image.localFile.childImageSharp;

    // only supporting fluid right now:
    const fluid = (childImageSharp.fluid =
      childImageSharp.fluid || {});
    fluid.aspectRatio =
      mediaDetails.width && mediaDetails.height
        ? mediaDetails.width / mediaDetails.height
        : undefined;
    fluid.originalImg = image.sourceUrl;
    fluid.originalName = mediaDetails.file;
    fluid.presentationHeight =
      fluid.aspectRatio && maxWidth
        ? Math.round(maxWidth / fluid.aspectRatio)
        : mediaDetails.height;
    fluid.presentationWidth = maxWidth;
    fluid.sizes = `(max-width: ${maxWidth}px) 100vw, ${maxWidth}px`;

    const srcSets = [];
    const allowedSizes = ["medium", "medium_large", "large"];
    mediaDetails.sizes.forEach((size) => {
      if (allowedSizes.indexOf(size.name) >= 0) {
        if (
          size.width === `${fluid.presentationWidth}`
        ) {
          fluid.src = size.sourceUrl;
        }
        srcSets.push(`${size.sourceUrl} ${size.width}w`);
      }
    });

    fluid.srcSet = srcSets.join(",\n");
  } else {
    console.warn("Unable to attach dynamic image sharp: Missing mediaDetails.");
  }
}

Вы будете вызывать функцию следующим образом (это также хорошее место для прикрепления общего изображения base64):

posts.nodes.forEach((post) => {
  attachChildImageSharp(post.featuredImage.node, 768);
   post.featuredImage.node.localFile.childImageSharp.fluid.base64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQYV2NgYGD4DwABBAEAcCBlCwAAAABJRU5ErkJggg= = "; // 1x1 black PNG, from https://shoonia.github.io/1x1/#000000ff
});

Остерегайтесь того, что неизменяемый объект вызовет ошибки, поэтому отключите кеширование (fetchPolicy: 'no-cache', если вы используете ApolloClient). Выберите ширину в качестве второго аргумента, которая является одной из трех значений ширины, выбранных вами в настройках Wordpress для размеров изображения (не включая миниатюру).

Это решение предназначено для Gatsby Image V1, и, судя по всему, оно не идеально, но удовлетворяет мои потребности.

Другие вопросы по теме