Я следовал руководству 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.
Любая идея, как я могу получить изображение из места, отличного от каталога уценки после публикации?


Причина в схеме вашего сервера, по которой вы, возможно, объявили переменную 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 части:
markdownRemark.frontmatter.featureImage, чтобы graphql разрешался в узел File вместо строки через createTypes@fileByDataPath через createFieldExtensionПрямо сейчас Гэтсби выводит 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.
Давайте просто посмотрим на атрибут разрешения. Это функция, которая принимает следующее:
frontmatter)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 г.), обновил ответ, чтобы отразить это.
Работает как шарм, мне пришлось переименовать featureImage в FeatureImage, чтобы заставить его работать с моими заголовками... И его можно использовать для других целей!!!
Есть ли способ получить относительный путь к уценке? У меня есть "./pic.jpb", но я не могу определить абсолютный путь к файлу уценки.
В дополнение к Derek Answer, который позволяет использовать активы любого типа в любом месте (звук, видео, gpx, ...), если вы ищете решение только для изображений, можно использовать:
https://www.gatsbyjs.org/packages/gatsby-remark-relative-images/
Хорошо, я не знал, что gatsby-remark-relative-images также может обрабатывать пути во фронтмастере, аккуратно!
Я не думаю, что это точное решение для журнала ошибок, о котором упоминал автор сообщения.