在 Gatsby 中实现这一点曾经非常麻烦,但是由于新的createSchemaCustomizationNode 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)
featureImageargs:在查询中传递给的参数。我们不需要这个
- 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 月),更新了答案以反映这一点。