盖茨比:多种内容类型

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/projectsscr/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
        }
      }
    }
  }
}