让我们来实践华丽的Gatsby.js,尝试改变数据类型
定制GraphQL模式
最近,我在尝试使用GraphQL来管理Markdown文件中的img时,遇到了类型推断错误,需要修改GraphQL的模式。因此,我需要学习这个指南!由于我的意译偏激,请谅解?♂️
需要基本了解GraphQL的模式语言
在这之前,其实我不知道GraphQL的写法,所以我会大致学习一下。我在上面链接的文章中进行了学习!
特别需要掌握的知识是有关于模式语言(用于定义Web API规范的)中的字段定义方法和接口的知识。
type Query {
currentUser: User!
}
interface Node {
id: ID!
}
接口是一个抽象的类型,它捆绑了一组必需的字段。
公式指南
我会查看官方指南。
这个指南
-
- プラグインの作成者
-
- 自動型類推によって作成されたスキーマを修正したい方(私)
-
- ビルドの最適化を測る方
- スキーマに興味のある方
看起来是针对…的。虽然这里很长,但我们只处理前一部分。
如果你对此感兴趣,读下去可能会有好处!
GraphQL的优势在于可以同时处理多个信息源!为了实现这一点,必须生成GraphQL模式。
Markdown和JSON的例子
从Markdown文件生成博客文章,并使用JSON格式提供作者信息。有时候,某些撰稿人的信息会单独保存为另一个JSON文件。
---
title: Sample Post
publishedAt: 2019-04-01
author: jane@example.com
tags:
- wow
---
# Heading
Text
[
{
"name": "Doe",
"firstName": "Jane",
"email": "jane@example.com",
"joinedAt": "2018-01-01"
}
]
[
{
"name": "Doe",
"firstName": "Zoe",
"email": "zoe@example.com",
"receivedSwag": true
}
]
为了使用GraphQL查询这些内容,我们需要使用gatsby-source-filesystem、gatsby-transformer-remark和gatsby-transformer-json插件。这些插件的内部操作是将markdown文件转换为具有唯一id和MarkdownRemark类型的节点对象。
作者数据被转换为 AuthorJson 类型的节点对象,贡献者数据被转换为 ContributorJson 类型的节点对象。
节点接口
以下是由 Source 插件和 Transformer 插件创建的 Gatsby 的 GraphQL 模式。
它包括了 Id、parent、children 这些字段,还有 internal 类型字段。
interface Node {
id: ID!
parent: Node!
children: [Node!]!
internal: Internal!
}
type Internal {
type: String!
}
** TODO: internal是什么?即使我查了也不是很清楚。
请提供一个例子,命名为autor.json。
Gatsby-transformer-json用于创建autor.json的节点类型的示例如下所示。
type AuthorJson implements Node {
id: ID!
parent: Node!
children: [Node!]!
internal: Internal!
name: String
firstName: String
email: String
joinedAt: Date
}
要查看Gatsby所创建的模式实际上,GraphQL Playground是一个推荐的选择。
在项目中
$ GATSBY_GRAPHQL_IDE=playground gatsby develop
比我想象中更加紧密。
自动推理
让我们来看一下之前的author.json文件。
[
{
"name": "Doe",
"firstName": "Jane",
"email": "jane@example.com",
"joinedAt": "2018-01-01"
}
]
尽管没有进行任何类型定义,但GraphQL可以很好地运行。这是因为它可以进行类型推断。通过检查每个字段的内容并检查类型来实现。
然而,这种类型推断有两个问题点。
-
- 時間がかかり、負担がかかる
- 型とデータが異なっていた場合、型の類推が失敗する
关于第二点,我会举例说明。
+ {
+ "name": "Doe",
+ "firstName": "John",
+ "email": "john@example.com",
+ "joinedAt": "201-04-02"
+ }
]
加入部分存在拼写错误,导致无法解释为”日期”。
为了解决这些问题,需要明确指示类型。
类型定义
使用createType action可以明确地定义类型。
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
const typeDefs = `
type AuthorJson implements Node {
joinedAt: Date
}
`
createTypes(typeDefs)
}
请注意不需要定义所有类型(如name、firstName等)的点。
有些人可能会坚持不进行类型推断!通过这样做可以提高性能。正如之前提到的,由于类型推断的负担很大,所以在大型项目中性能下降明显。可以使用@dontInfer指令来选择性地取消类型推断。
对于不清楚什么是指令的人,请看一下之前关于GraphQL架构的解释:https://employment.en-japan.com/engineerhub/entry/2018/12/26/103000
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
const typeDefs = `
type AuthorJson implements Node @dontInfer {
name: String!
firstName: String!
email: String!
joinedAt: Date
}
`
createTypes(typeDefs)
}
这是以节制版本进行的。
关于Node接口提供的字段,不需要明确指定,因为Gatsby会帮忙添加。(如id、parent等)
嵌套类型
在过去,我们只处理了标量类型,例如 String 类型和 Date 类型。在 GraphQL 中,还有其他类型可供处理。
-
- ID型
-
- Int型
-
- Float型
-
- Boolean型
- JSON型
也可以处理诸如此类的内容。而且,还可以处理复杂的对象值。
让我们以markdown-remark为例。
根据以下描述,frontmatter.tags将始终是一个字符串数组。
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
const typeDefs = `
type MarkdownRemark implements Node {
frontmatter: Frontmatter
}
type Frontmatter {
tags: [String!]!
}
`
createTypes(typeDefs)
}
如果直接指定Frontmatter类型,则会失败,就像上述描述的那样。
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
const typeDefs = `
# これは失敗!!!
type Frontmatter {
tags: [String]!
}
`
createTypes(typeDefs)
}
因为Frontmatter类型由源插件或转换插件创建,因此Node接口无法实现。
为了这个原因,会进行这样的处理。