graphql - 使用 Gatsby 的 Markdown 帖子中的特色图像的绝对路径
问题描述
我已经按照 Gatsby 教程在 Markdown 帖子和页面中处理图像,它运行良好,但我想要实现的是从静态位置获取图像,而不是使用图像的相对路径。
想参考这样的图像(在frontmatter中)
featuredImage: img/IMG_20190621_112048_2.jpg
IMG_20190621_112048_2.jpg位于而/src/data/img
不是与markdown文件相同的目录下 /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/`,
},
},
但 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”不能有选择,因为“字符串”类型没有子字段。
知道如何从与 post markdown 目录不同的位置获取图像吗?
解决方案
在 Gatsby 中实现这一点曾经非常麻烦,但是由于新的createSchemaCustomization
Node API文档(自 Gatsby 2.5 起),它相对容易。
这是一个演示,我在其中复制了您的 repo 结构: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 解析为 File 节点而不是字符串 viacreateTypes
@fileByDataPath
通过创建一个新的字段扩展createFieldExtension
创建类型
现在盖茨比的推断frontmatter.featureImage
是一个字符串。我们将要求 Gatsby 将 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
}
}
}
}
我们得到...
错误:不能为不可为空的字段 File.id 返回 null。
这是因为虽然 Gatsby 现在知道featureImage
应该是一个 File 节点,但它不知道从哪里获取该文件。
此时,我们既可以使用createResolvers
手动将字段解析为 File 节点,也createFileExtension
可以执行相同的操作。我选择createFileExtension
它是因为它允许更多的代码重用(您可以扩展任何字段),而createResolvers
在这种情况下,对于特定字段更有用。看到你想要的只是从src/data
目录中解析一个文件,我将调用这个扩展名fieldByDataPath
。
创建文件扩展
让我们看一下resolve属性。它是一个接受以下内容的函数:
- source:父字段的数据(本例中为
frontmatter
) featureImage
args:在查询中传递给的参数。我们不需要这个- context: contains
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% 的工作。最后要做的是移动 this 以将此解析函数传递给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/
frontmatter 中的相对路径。
额外的
实现方式fileByDataPath
,它只适用于名为featureImage
. 这不是太有用,所以我们应该修改它,使其适用于任何字段,例如,名称以_data
;结尾的字段。或者至少接受要处理的字段名称列表。
Edit手头有一点时间,所以我写了一个插件来做这个,还写了一个博客。
编辑 2 Gatsby 此后进行了runQuery
异步(2020 年 7 月),更新了答案以反映这一点。
推荐阅读
- java - Camel Jetty 使用 Spring Boot 端口
- java - Spring Boot REST:无法测试@RequestParam 的验证
- go - 如何在 vscode 中调试 goroutine?
- cordova - 在 iframe 中显示位于 www 文件夹之外的 html 文件——在 InAppBrowser 中工作或解析文本
- python - 在 icrawler 中使用 BingImageCrawler 下载 1000 多张图片
- python - 熊猫条件语句
- php - 如何在 laravel 的 mysql 查询生成器中转换为标题大小写
- python - for循环中的python字典列表返回所有相同的值
- ruby-on-rails - 如何创建任务以根据最后一个孩子的日期更新父母的属性
- java - 代码一直说我没有在我的代码中为猜谜游戏初始化我的字符串变量“restart”