使用 Gatsby 的 Markdown 帖子中的特色图像的绝对路径

Posted

技术标签:

【中文标题】使用 Gatsby 的 Markdown 帖子中的特色图像的绝对路径【英文标题】:Use absolute path for featured image in markdown post with Gatsby 【发布时间】: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”类型没有子字段。

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

【问题讨论】:

【参考方案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)

工作原理:

这有两个部分:

    扩展markdownRemark.frontmatter.featureImage,以便graphql 通过createTypes 解析为文件节点而不是字符串 通过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

谢谢

瑞金预兆

【讨论】:

以上是关于使用 Gatsby 的 Markdown 帖子中的特色图像的绝对路径的主要内容,如果未能解决你的问题,请参考以下文章

博客文章中的 Gatsby-js 客户端 javascript 内联

如何处理 GatsbyJS 中 markdown 帖子之间的父/子关系

尝试使用 Contentful 将帖子列表添加到我的主页而不是 Gatsby 中的单独页面

Gatsby 使用 slug 查询特定帖子

Gatsby:在自定义帖子类型上使用 GraphQL 查询和自定义分类

Gatsby 如何使用 GraphQL 提供 Markdown frontmatter?