Используйте абсолютный путь для избранного изображения в посте уценки с Гэтсби

Я следовал руководству Gatsby для Работа с изображениями в сообщениях и страницах Markdown, которое работает хорошо, но я хочу получить изображение из статического местоположения вместо использования относительного пути для изображения.

Хотел бы сослаться на такое изображение (в переднем плане)

featuredImage: img/IMG_20190621_112048_2.jpg

Где IMG_20190621_112048_2.jpg находится в /src/data/img вместо того же каталога, что и файл уценки под /src/posts

Я пытался настроить 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/`,
  },
},

но запрос graphQL в шаблоне сообщения не работает:

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

GraphQL Error Field "featuredImage" must not have a selection since type "String" has no subfields.

Любая идея, как я могу получить изображение из места, отличного от каталога уценки после публикации?

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Что такое Apollo Client и зачем он нужен?
Что такое Apollo Client и зачем он нужен?
Apollo Client - это полнофункциональный клиент GraphQL для JavaScript-приложений, который упрощает получение, управление и обновление данных в...
4
0
2 293
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Причина в схеме вашего сервера, по которой вы, возможно, объявили переменную FeaturedImage как строку, а в запросе вашего клиента graphql вы пытаетесь вызвать подобъекты переменной FeatureImage, и эти подобъекты не существуют.

Возможно, вам придется проверить определение схемы graphql и согласовать запрос с определением схемы.

ваша текущая схема может быть такой

featuredImage: String

и вам нужно изменить его, объявив правильные типы на основе требований на стороне сервера.

Для получения дополнительной информации о типах graphql. пожалуйста, укажите этот URL - https://graphql.org/learn/schema/#object-types-and-fields

Спасибо

Ригин Ооммен

Ответ принят как подходящий

Добиться этого в Gatsby раньше было довольно проблематично, но благодаря новому createSchemaCustomization Node API docs (начиная с Gatsby 2.5) это относительно просто.

Вот демонстрация, в которой я воспроизвел вашу структуру репо: гитхаб

Вот где живет соответствующий код: гитхаб

Вот код, чтобы заставить его работать:

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

Как это работает:

В этом есть 2 части:

  1. Расширьте markdownRemark.frontmatter.featureImage, чтобы graphql разрешался в узел File вместо строки через createTypes
  2. Создайте новое расширение поля @fileByDataPath через createFieldExtension

createTypes

Прямо сейчас Гэтсби выводит frontmatter.featureImage как строку. Вместо этого мы попросим Гэтсби читать featureImage как строку, изменив его родительский тип:

  type Frontmatter {
    featureImage: File
  }

Однако этого недостаточно, нам также нужно передать этот тип Frontmatter его родителю:

  type Frontmatter {
    featureImage: File
  }

  type MarkdownRemark implements Node {
    frontmatter: Frontmatter
  }

Мы также добавим тег @infer, который сообщает Gatsby, что он может выводить другие поля этих типов, например frontmatter.title, markdownRemark.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
      }
    }
  }
}

и получаем...

Error: Cannot return null for non-nullable field File.id.

Это потому, что, хотя Gatsby теперь понимает, что featureImage должен быть узлом File, он понятия не имеет, где взять этот файл.

На этом этапе мы можем либо использовать createResolvers, чтобы вручную преобразовать поле в узел File, либо createFileExtension, чтобы сделать то же самое. Я выбираю createFileExtension, потому что он позволяет больше повторно использовать код (вы можете расширять любые поля), а createResolvers в данном случае более полезен для конкретного поля. Видя, что все, что вам нужно, это разрешить файл из каталога src/data, я назову это расширение fieldByDataPath.

создатьFileExtension

Давайте просто посмотрим на атрибут разрешения. Это функция, которая принимает следующее:

  • источник: данные родительского поля (в данном случае frontmatter)
  • args: аргументы, переданные featureImage в запросе. Нам это не понадобится
  • контекст: содержит nodeModel, который мы будем использовать для получения узлов из хранилища узлов Gatsby.
  • информация: метаданные об этом поле + вся схема

Мы найдем исходный путь (img/photo.jpg) из src.featureImage, затем приклеим его к 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 // <---
  }
  ...
`

При этом теперь вы можете использовать относительный путь от src/data/ во фронтмейтере.

Дополнительный

То, как реализовано fileByDataPath, будет работать только с полями с именем featureImage. Это не слишком полезно, поэтому мы должны изменить его так, чтобы он работал с любым полем, имя которого, скажем, оканчивается на _data; или, по крайней мере, примите список имен полей для работы.

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

Редактировать 2 Гэтсби с тех пор сделал runQuery асинхронным (июль 2020 г.), обновил ответ, чтобы отразить это.

Я не думаю, что это точное решение для журнала ошибок, о котором упоминал автор сообщения.

Rigin Oommen 16.10.2019 12:11

Работает как шарм, мне пришлось переименовать featureImage в FeatureImage, чтобы заставить его работать с моими заголовками... И его можно использовать для других целей!!!

avianey 17.10.2019 13:59

Есть ли способ получить относительный путь к уценке? У меня есть "./pic.jpb", но я не могу определить абсолютный путь к файлу уценки.

07mm8 27.10.2020 03:54

В дополнение к Derek Answer, который позволяет использовать активы любого типа в любом месте (звук, видео, gpx, ...), если вы ищете решение только для изображений, можно использовать:

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

Хорошо, я не знал, что gatsby-remark-relative-images также может обрабатывать пути во фронтмастере, аккуратно!

Derek Nguyen 17.10.2019 16:21

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