【问题标题】:Use absolute path for featured image in markdown post with Gatsby使用 Gatsby 的 Markdown 帖子中的特色图像的绝对路径
【发布时间】:2019-11-30 19:39:55
【问题描述】:

我遵循了 Working With Images in Markdown Posts and Pages 的 Gatsby 教程,它运行良好,但我想要实现的是从静态位置获取图像,而不是使用图像的相对路径。

想参考这样的图片(在frontmatter中)

featuredImage: img/IMG_20190621_112048_2.jpg

其中IMG_20190621_112048_2.jpg/src/data/img而不是与/src/posts下的markdown文件相同的目录

我尝试像这样设置gatsby-source-filesystem

{
  resolve: `gatsby-source-filesystem`,
  options: {
    name: `posts`,
    path: `${__dirname}/src/posts`,
  },
},
{
  resolve: `gatsby-source-filesystem`,
  options: {
    name: `data`,
    path: `${__dirname}/src/data/`,
  },
},

但 post 模板中的 graphQL 查询失败:

export const query = graphql`
  query($slug: String!) {
    markdownRemark(fields: { slug: { eq: $slug } }) {
      html
      frontmatter {
        title
        featuredImage {
          childImageSharp {
            fluid(maxWidth: 800) {
              ...GatsbyImageSharpFluid
            }
          }
        }
      }
    }
  }

GraphQL 错误字段“featuredImage”不能有选择,因为 “String”类型没有子字段。

知道如何从与降价后目录不同的位置获取图像吗?

【问题讨论】:

    标签: graphql gatsby


    【解决方案1】:

    过去在 Gatsby 中实现这一点相当麻烦,但感谢新的 createSchemaCustomization Node API docs(自 Gatsby 2.5 起)相对容易。

    这是我复制您的回购结构的演示:github

    这里是相关代码所在的位置:github

    这是使它工作的代码:

    // gatsby-node.js
    
    const path = require('path')
    
    exports.createSchemaCustomization = ({ actions }) => {
      const { createFieldExtension, createTypes } = actions
    
      createFieldExtension({
        name: 'fileByDataPath',
        extend: () => ({
          resolve: function (src, args, context, info) {
            const partialPath = src.featureImage
              if (!partialPath) {
                return null
              }
    
            const filePath = path.join(__dirname, 'src/data', partialPath)
            const fileNode = context.nodeModel.runQuery({
              firstOnly: true,
              type: 'File',
              query: {
                filter: {
                  absolutePath: {
                    eq: filePath
                  }
                }
              }
            })
    
            if (!fileNode) {
              return null
            }
    
            return fileNode
          }
        })
      })
    
      const typeDefs = `
        type Frontmatter @infer {
          featureImage: File @fileByDataPath
        }
    
        type MarkdownRemark implements Node @infer {
          frontmatter: Frontmatter
        }
      `
    
      createTypes(typeDefs)
    }
    

    工作原理:

    这有两个部分:

    1. 扩展markdownRemark.frontmatter.featureImage,以便graphql 通过createTypes 解析为文件节点而不是字符串
    2. 通过createFieldExtension 创建一个新的字段扩展@fileByDataPath

    创建类型

    现在 Gatsby 将 frontmatter.featureImage 推断为字符串。我们将要求 Gatsby 将 featureImage 读取为字符串,方法是修改其父类型:

      type Frontmatter {
        featureImage: File
      }
    

    但这还不够,我们还需要将这个 Frontmatter 类型也传递给它的父对象:

      type Frontmatter {
        featureImage: File
      }
    
      type MarkdownRemark implements Node {
        frontmatter: Frontmatter
      }
    

    我们还将添加@infer 标签,让 Gatsby 知道它可以推断这些类型的其他字段,即frontmatter.titlemarkdownRemark.html 等。

    然后将这些自定义类型传递给createTypes

    exports.createSchemaCustomization = ({ actions }) => {
      const { createTypes } = actions
    
      const typeDefs = `
        type Frontmatter @infer {
          featureImage: File
        }
    
        type MarkdownRemark implements Node @infer {
          frontmatter: Frontmatter
        }
      `
    
      createTypes(typeDefs)
    }
    

    现在,我们可以启动localhost:8000/___graphql 并尝试查询图像

    query Post {
      markdownRemark {
        frontmatter {
          featureImage {
            id
          }
        }
      }
    }
    

    我们得到...

    错误:不能为不可为空的字段 File.id 返回 null。

    这是因为虽然 Gatsby 现在知道 featureImage 应该是一个文件节点,但它不知道从哪里获取该文件。

    此时,我们既可以使用createResolvers 手动将字段解析为文件节点,也可以使用createFileExtension 来做同样的事情。我选择createFileExtension 是因为它允许更多代码重用(您可以扩展任何字段),而在这种情况下,createResolvers 对于特定字段更有用。看到你想要的只是从src/data目录解析一个文件,我将这个扩展称为fieldByDataPath

    创建文件扩展

    让我们看看 resolve 属性。它是一个接受以下内容的函数:

    • 来源:父字段的数据(本例为frontmatter
    • args:在查询中传递给featureImage 的参数。我们不需要这个
    • context:包含nodeModel,我们将使用它从 Gatsby 节点存储中获取节点
    • 信息:关于此字段的元数据 + 整个架构

    我们会从src.featureImage中找到原始路径(img/photo.jpg),然后将其粘到src/data,得到一个完整的绝对路径。接下来,我们查询 nodeModel 以找到具有匹配绝对路径的 File 节点。由于您已经将gatsby-source-filesystem 指向src/data,因此图像(photo.jpg)将在 Gatsby 节点存储中。

    如果我们找不到路径或匹配节点,请返回null

      resolve: async function (src, args, context) {
        // look up original string, i.e img/photo.jpg
        const partialPath = src.featureImage
          if (!partialPath) {
            return null
          }
    
        // get the absolute path of the image file in the filesystem
        const filePath = path.join(__dirname, 'src/data', partialPath)
        
        // look for a node with matching path
        const fileNode = await context.nodeModel.runQuery({
          firstOnly: true,
          type: 'File',
          query: {
            filter: {
              absolutePath: {
                eq: filePath
              }
            }
          }
        })
    
        // no node? return
        if (!fileNode) {
          return null
        }
    
        // else return the node
        return fileNode
      }
    

    我们已经完成了 99% 的工作。最后要做的是移动它以将此解析函数传递给createFieldExtension;以及将新扩展添加到 createTypes

    createFieldExtension({
      name: 'fileByDataPath' // we'll use it in createTypes as `@fileByDataPath`
      extend: () => ({
        resolve,             // the resolve function above
      })
    })
    
    const typeDef = `
      type Frontmatter @infer {
        featureImage: File @fileByDataPath // <---
      }
      ...
    `
    

    这样,您现在可以在 frontmatter 中使用来自 src/data/ 的相对路径。

    额外

    fileByDataPath 的实现方式仅适用于名为 featureImage 的字段。这不是太有用,所以我们应该修改它,使其适用于任何字段,例如,名称以_data 结尾的字段;或者至少接受要处理的字段名称列表。

    编辑我手头有一点时间,所以我wrote a plugin 这样做了,还有wrote a blog on it

    编辑 2 此后,Gatsby 使 runQuery 异步(2020 年 7 月),更新了答案以反映这一点。

    【讨论】:

    • 我不认为这是帖子作者提到的错误日志的确切解决方案
    • 像一个魅力一样工作,我不得不将 featureImage 重命名为 featuresImage 以使其与我的 frontmatter 标题一起使用......并且可以用于其他方式!!!
    • 有什么方法可以获取markdown的相对路径?我有“./pic.jpb”,但我不知道markdown文件的绝对路径。
    【解决方案2】:

    除了允许在任何地方使用任何类型的资产(声音、视频、gpx...)的 Derek Answer 之外,如果只为图像寻找解决方案,可以使用:

    https://www.gatsbyjs.org/packages/gatsby-remark-relative-images/

    【讨论】:

    • 很好,我不知道 gatsby-remark-relative-images 也可以处理 frontmatter 中的路径,整洁!
    【解决方案3】:

    在您的服务器模式中,您可能已将 featuresImage 变量声明为字符串,而在您的客户端 graphql 查询中,您试图调用 featuresImage 变量的子对象并且该子对象不存在。

    您可能需要检查 graphql 架构定义并将查询与架构定义对齐

    你当前的架构可能是这样的

    featuredImage: String
    

    您需要根据服务器端的要求声明适当的类型来更改它。

    有关 graphql 类型的更多信息。请参考这个网址 - https://graphql.org/learn/schema/#object-types-and-fields

    谢谢

    瑞金预兆

    【讨论】:

      猜你喜欢
      • 2021-02-09
      • 2019-07-06
      • 2020-02-05
      • 2016-09-03
      • 2020-08-07
      • 2021-05-04
      • 2012-08-08
      • 1970-01-01
      • 2011-09-05
      相关资源
      最近更新 更多