Я подумываю использовать 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 фактически извлекает и анализирует запросы заранее и не оценивает динамический код при этом. Похоже на довольно конкретное техническое соображение. Источник: github.com/gatsbyjs/rfcs/pull/3
Хм, хорошо, я понимаю. Поиск продолжается, я думаю! Спасибо



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Я тоже искал этот ответ. Надеюсь, это ответит на ваш вопрос:
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%, влияет ли это на время компиляции, поскольку мы сканируем все изображения. Если это так, пожалуйста, дайте мне знать!
Обновление: если у вас много изображений, размер пакета может стать довольно большим, так как это решение сканирует ВСЕ изображения.
Вы можете настроить код так, чтобы отображалось изображение-заполнитель, если реквизиты не переданы.
Тем не менее, есть другой способ справиться с этим, но с немного большей работой/кодом.
Команда Гэтсби действительно упустила мяч. Кто бы мог подумать, что веб-разработчики не захотят создавать отдельный запрос и компонент для каждого отдельного изображения. /с
Это приятно. Обратите внимание, что вы платите за пакет javascript со всеми этими изображениями. Это не проблема, если они все используются. Однако, если вы добавите 2-3 компонента изображения, чтобы получить разные размеры (разные варианты), вы оплатите всю эту стоимость в комплекте. -- Так что это будет работать хорошо, если вы можете сохранить одинаковые параметры для всех изображений И использовать почти все изображения в запросе graphql.
Я использую allFile с относительным каталогом. Поэтому я могу отображать только изображения в определенной подпапке. allFile(filter: {relativeDirectory: {eq: "team"}}) {...}
fluid также устарела.
К сожалению, из того, что я могу собрать, лучшим решением является запись отдельных файлов 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 для настоящих динамических данных. Ваш ответ — хорошее напоминание, но не решение проблемы.
Да, это метод, используемый во всей экосистеме Гэтсби, поскольку всегда наиболее эффективно прикреплять данные перед сборкой (для повышения производительности). Очевидно, что это не работает для внешних запросов, изображения и данные должны быть известны во время сборки. Не могли бы вы объяснить, почему это не решение проблемы? Редко бывает, что данные изображения не известны во время сборки. Вы можете избежать компонента, который извлекает данные, запросив данные раньше.
Что ж, здорово, что мы согласны с первой частью. Однако я не говорил, что ваше решение не является решением проблемы. Я сказал, что это не решение, когда вы хотите обрабатывать «настоящие динамические данные». Так что, если вы строги и хотите использовать компонент Gatsby <Img> «как обычный тег img» (как указано в вопросе), это не сработает. Это все еще неплохо для людей, которым не нужно показывать настоящие динамические изображения, но как только кто-то реализует, например, вызов AJAX «загрузить больше продуктов», который включает показ изображений добавленных продуктов с использованием Gatsby Image, он не может работать.
Ах да, это полностью обходит решение для внешних компонентов и не поддерживает вызов данных на стороне клиента; все данные потребуются во время сборки. Спасибо за разъяснения.
Если вы используете Wordpress с WP GraphQL и хотите динамически загружать некоторые сообщения, вы столкнетесь с той же проблемой. Вы не сможете использовать замечательные функции предварительной обработки, такие как снижение качества и использование грубых заполнителей base64. Как уже упоминалось, решение @RodrigoLeon будет работать, но вы столкнетесь с огромной нагрузкой, если ваш сайт в конечном итоге разрастется за счет изображений.
Поскольку на моем сайте будет много постов и будет динамическая загрузка таких постов, мне пришлось придумать решение, которое было бы, по крайней мере, приемлемым. В итоге я генерирую childImageSharp (и предоставляю общий заполнитель base64) для динамической части моего веб-сайта, поэтому я всегда могу передать childImageSharp компоненту <Img> Gatsby.
Вот пример гибкого типа изображения для избранных изображений в сообщениях:
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, и, судя по всему, оно не идеально, но удовлетворяет мои потребности.
Поскольку
gatsby-imageтребует предварительной обработки от самого gatsby, невозможно сделать его по-настоящему динамичным во время выполнения, как с тегомimg. Вы должны использоватьStaticQueryили получить данные в обычном запросе страницы.