具有动态图像源的可重复使用的盖茨比图像组件

时间:2019-03-12 13:33:41

标签: javascript reactjs graphql gatsby

我正在考虑将Gatsby-Image用于我的下一个项目,并且已经进行了一些尝试。

我可以在测试项目中使用它,但是后来我想到了一个用例,我想像常规的<img src”image.png”>标签一样使用Gatsby中的。因此,我的问题是如何使Gatsby组件可重用?

import React from "react"
import { StaticQuery, graphql } from "gatsby"
import Img from "gatsby-image"
function renderImage({ file }) {
  console.log({ 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文件中定义的relativePath发出动态请求,然后将每个Gatsby中的src prop组合在一起,并将其与我的资产文件中的所有图像匹配,然后显示它。你们中有人知道这是否可能吗?

我在文档中读到静态查询不能接受变量-仅页面。但是我不希望我的图像与页面相关联-我想在任何需要的地方使用此组件-像常规的img标签一样。

希望我已经说清楚了。请问您是否有任何疑问。

这是一个示例: https://codesandbox.io/s/py5n24wk27

预先感谢, 埃里克

4 个答案:

答案 0 :(得分:4)

我正在构建的网站是一个电子商务平台,其中包含数千张图片(适用于所有产品)。这是使用gatsby查询图像的主要问题。很长时间以来,我有一个查询所有图像并将其与各自产品匹配的组件。 (就像在此建议的那样)这是非常低效的,会引发有关查询持续时间的警告。

一种替代方法是在数据级别上将imageFile附加到产品上,而不是在尝试呈现时。

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)
}

src / components / image / 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是我要将文件附加到的节点的类型。 (可以在localhost的graphql游乐场中找到)。解析将使Key的{​​{1}}与产品上的S3Object(是一个字符串)匹配。这使我可以直接使用产品图像,而不必在图像组件内部运行查询。

在我看来,这是一条有价值的信息,一旦您将其包裹住,肯定会对我有很大帮助。

答案 1 :(得分:2)

我也一直在寻找这个答案。希望这能回答您的问题:

最终代码:

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%是否会影响编译时间。如果有,请告诉我!

进一步的自定义

如果没有传递任何道具,则可以调整代码以显示占位符图像。

替代品

那是there is another way you could tackle this,但工作/代码更多。

来源

  • 我修改了this article中的代码。 (请注意,本文使用的是不推荐使用的代码。)

答案 2 :(得分:2)

所以@RodrigoLeon,我注意到,按照您的方法进行操作,将导致捆绑包的大小急剧增加。尤其是如果您说您拥有超过50张图片。因为任何时候您使用它并遍历所有图像,您便​​在组件文件中创建了对它们的引用。因此,我不建议您这样做。不幸的是,据我所知,最好的解决方案是为图像写出单独的js文件。

答案 3 :(得分:0)

如果您将 Wordpress 与 WP GraphQL 结合使用,并且想要动态加载一些帖子,您将面临同样的问题。您将无法使用出色的预处理功能,例如降低质量和使用粗略的 base64 占位符。正如前面提到的,@RodrigoLeon 解决方案是可行的,但如果您的网站最终在图片中增长,您将面临巨大的负载。

由于我的网站将包含大量帖子,并且会动态加载此类帖子,因此我必须想出一个至少可以接受的解决方案。我最终要做的是为我网站的动态部分生成 childImageSharp(并提供一个通用的 base64 占位符),因此我总是可以将 childImageSharp 传递给 Gatsby 的 <Img> 组件。

以下是帖子中精选图片的流体图片类型示例:

  • 确保将其包含在您的 GQL 中:
featuredImage {
  node {
    sourceUrl
    mediaDetails {
      file
      width
      height
      sizes {
        file
        name
        width
        sourceUrl
      }
    }
  }
}

加载你的帖子后,通过这个函数发送你的文件节点(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,并且经过所有尝试,它并不完美,但可以满足我的需求。