盖茨比:多种内容类型
Gatsby: Multiple Content Types
我试图加快 Gatsby 的速度并在演示中取得了巨大成功,但是 运行 我觉得这是一个相对常见且简单的用例。我想要可以在 Markdown 中编写的多种内容类型,每种都有不同的 Frontmatter,每种都有不同的模板。
例如,我想要一个 BlogPost 内容类型和一个项目内容类型:
博文内容类型
---
title: My Post
date: "2017-09-21"
---
This is my blog body
项目内容类型
---
projectName: My Project
startDate: "2017-09-21"
endDate: "2017-10-21"
---
This is my project description
然后为了让它们在相关模板中呈现,我不得不在 gatsby-node.js
中使用正则表达式做一些棘手的事情:
const components = {
blog: `./src/templates/blog-post.js`,
projects: `./src/templates/project-post.js`,
}
exports.createPages = ({ graphql, boundActionCreators }) => {
const { createPage } = boundActionCreators
RE_DIR = new RegExp("\/pages\/([a-z]+)\/.*\.md$");
return new Promise((resolve, reject) => {
graphql(`
{
allMarkdownRemark {
edges {
node {
fileAbsolutePath
fields {
slug
}
}
}
}
}
`).then(result => {
result.data.allMarkdownRemark.edges.forEach(({ node }) => {
// console.log(RE_DIR.exec(node.fileAbsolutePath))
const postType = RE_DIR.exec(node.fileAbsolutePath)[1]
if (postType) {
createPage({
path: node.fields.slug,
component: path.resolve(components[postType]),
context: {
// Data passed to context is available in page queries as GraphQL variables.
slug: node.fields.slug,
},
})
}
})
resolve()
})
})
};
我现在遇到的问题是,由于 frontmatter 不一致,GraphQL 似乎只从其中一个来源获取 frontmatter 模式。
是否有更简单的方法来拥有多种内容类型?
在 gatsby-config
中定义不同的来源,并将您的内容放在不同的目录中,例如 src/projects
和 scr/blog-posts
{
resolve: `gatsby-source-filesystem`,
options: {
name: `project`,
path: `${__dirname}/src/project/`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `posts`,
path: `${__dirname}/src/blog-posts/`,
},
},
然后可以根据gatsby-node
中的源名称创建字段类型
exports.onCreateNode =({ node, getNode, boundActionCreators }) => {
if (node.internal.type === 'MarkdownRemark') {
const { createNodeField } = boundActionCreators;
node.collection = getNode(node.parent).sourceInstanceName;
}
}
现在您可以过滤 graphql 查询以获取内容集合,并且可以根据内容类型生成特定模板。
query postsOnly {
allMarkdownRemark(filter: { collection: { eq: "posts" } }) {
edges {
node {
id
collection
}
}
}
}
代码基于 github issue tracker
上的评论
注意: 你不应该直接在 gatsby-node
中改变 node 实例,而是使用 createNodeField
。如果您知道如何使用自定义字段在 graphql 中进行过滤,请添加到此答案中!
添加我的答案,其中基于@nicokant,但似乎有所改变。我在这里也使用 mdx,但如果您使用的是 MarkdownRemark
,则换成
:
为每个来源指定一个名称选项:
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/src/posts`,
name: 'post',
},
},
然后在创建节点时,为其分配一个自定义字段:
exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions
if (node.internal.type === `MarkdownRemark` || node.internal.type === `Mdx`) {
createNodeField({
name: `collection`,
node,
value: getNode(node.parent).sourceInstanceName
});
})
};
然后可以根据自定义字段查询:
query {
allMdx(filter: { fields: { collection: { eq: "post"}}}) {
edges {
node {
fields {
collection
}
frontmatter {
title
}
}
}
}
}
我试图加快 Gatsby 的速度并在演示中取得了巨大成功,但是 运行 我觉得这是一个相对常见且简单的用例。我想要可以在 Markdown 中编写的多种内容类型,每种都有不同的 Frontmatter,每种都有不同的模板。
例如,我想要一个 BlogPost 内容类型和一个项目内容类型:
博文内容类型
---
title: My Post
date: "2017-09-21"
---
This is my blog body
项目内容类型
---
projectName: My Project
startDate: "2017-09-21"
endDate: "2017-10-21"
---
This is my project description
然后为了让它们在相关模板中呈现,我不得不在 gatsby-node.js
中使用正则表达式做一些棘手的事情:
const components = {
blog: `./src/templates/blog-post.js`,
projects: `./src/templates/project-post.js`,
}
exports.createPages = ({ graphql, boundActionCreators }) => {
const { createPage } = boundActionCreators
RE_DIR = new RegExp("\/pages\/([a-z]+)\/.*\.md$");
return new Promise((resolve, reject) => {
graphql(`
{
allMarkdownRemark {
edges {
node {
fileAbsolutePath
fields {
slug
}
}
}
}
}
`).then(result => {
result.data.allMarkdownRemark.edges.forEach(({ node }) => {
// console.log(RE_DIR.exec(node.fileAbsolutePath))
const postType = RE_DIR.exec(node.fileAbsolutePath)[1]
if (postType) {
createPage({
path: node.fields.slug,
component: path.resolve(components[postType]),
context: {
// Data passed to context is available in page queries as GraphQL variables.
slug: node.fields.slug,
},
})
}
})
resolve()
})
})
};
我现在遇到的问题是,由于 frontmatter 不一致,GraphQL 似乎只从其中一个来源获取 frontmatter 模式。
是否有更简单的方法来拥有多种内容类型?
在 gatsby-config
中定义不同的来源,并将您的内容放在不同的目录中,例如 src/projects
和 scr/blog-posts
{
resolve: `gatsby-source-filesystem`,
options: {
name: `project`,
path: `${__dirname}/src/project/`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `posts`,
path: `${__dirname}/src/blog-posts/`,
},
},
然后可以根据gatsby-node
exports.onCreateNode =({ node, getNode, boundActionCreators }) => {
if (node.internal.type === 'MarkdownRemark') {
const { createNodeField } = boundActionCreators;
node.collection = getNode(node.parent).sourceInstanceName;
}
}
现在您可以过滤 graphql 查询以获取内容集合,并且可以根据内容类型生成特定模板。
query postsOnly {
allMarkdownRemark(filter: { collection: { eq: "posts" } }) {
edges {
node {
id
collection
}
}
}
}
代码基于 github issue tracker
上的评论注意: 你不应该直接在 gatsby-node
中改变 node 实例,而是使用 createNodeField
。如果您知道如何使用自定义字段在 graphql 中进行过滤,请添加到此答案中!
添加我的答案,其中基于@nicokant,但似乎有所改变。我在这里也使用 mdx,但如果您使用的是 MarkdownRemark
,则换成
为每个来源指定一个名称选项:
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/src/posts`,
name: 'post',
},
},
然后在创建节点时,为其分配一个自定义字段:
exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions
if (node.internal.type === `MarkdownRemark` || node.internal.type === `Mdx`) {
createNodeField({
name: `collection`,
node,
value: getNode(node.parent).sourceInstanceName
});
})
};
然后可以根据自定义字段查询:
query {
allMdx(filter: { fields: { collection: { eq: "post"}}}) {
edges {
node {
fields {
collection
}
frontmatter {
title
}
}
}
}
}