在 Gatsby 上创建第二个博客模板

Posted

技术标签:

【中文标题】在 Gatsby 上创建第二个博客模板【英文标题】:Creating a Second Blog Template on Gatsby 【发布时间】:2020-02-13 18:35:54 【问题描述】:

我正在尝试在我的 Gatsby 网站上创建第二个博客模板。我才刚刚开始学习 javascript,所以我需要 gatsby-node.js 文件的帮助。

我试图复制每一行函数以创建第二个博客模板,但每次都失败了,所以如果有人可以浏览我的代码并提供正确的函数,那将非常有帮助。

我目前有一个可以成功运行的“文章”模板。我想创建第二个“项目”模板。

这是我当前的 gatsby-node.js 文件,它只调用“文章”博客模板。


const path = require('path');
const  createFilePath  = require('gatsby-source-filesystem');

// Look at every node when it is created
exports.onCreateNode = (node, getNode, actions) => 
  // Check for markdown nodes
  const  createNodeField  = actions;
  if(node.internal.type === 'MarkdownRemark') 
    // Create a slug out of the markdown filepath name
    const slug = createFilePath(
      node,
      getNode,
      basePath: 'articles'
    );
    // Add the newly created slug to the node itself
    createNodeField(
      node,
      name: 'slug',
      value: `/article$slug`
    );
  
;

exports.createPages = ( graphql, actions ) => 
  const  createPage  = actions
  return new Promise((resolve, reject) => 
    graphql(`
    
      allMarkdownRemark 
      edges 
        node 
        fields 
          slug
        
        
      
      
    
    `).then(result => 
    result.data.allMarkdownRemark.edges.forEach(( node ) => 
      createPage(
      path: node.fields.slug,
      component: path.resolve(`./src/templates/article.js`),
      context: 
        // Data passed to context is available in page queries as GraphQL variables.
        slug: node.fields.slug,
      ,
      )
    )
    resolve()
    )
  )
  ;


如何配置此文件以调用第二个博客模板(“项目”)?

【问题讨论】:

【参考方案1】:

首先,您需要在您获得的 graphql 查询中检索您的文章以及您的项目。您可以使用冒号重命名查询的一部分。我在你的 allMarkdownRemark 之前添加了articles:。在生成的 JSON 中,allMarkdownRemark 现在将被称为“文章”。您可以在现在迭代“result.data.articles”的 forEach 循环中看到这一点。我还为您想要的“项目”实体添加了一个查询。将它们重命名为文章和项目使我们能够轻松地遍历每篇文章和每个项目,并为这些实体创建正确的页面。

项目查询现在是您的文章查询的副本。实际上,您应该在此处提供仅返回项目的查询,但我不知道您在哪里拥有这些项目。然后为您创建正确页面的项目和文章创建单独的循环。

exports.createPages = ( graphql, actions ) => 
    const  createPage  = actions

    return new Promise((resolve, reject) => 
        graphql(`
            articles: allMarkdownRemark 
                edges 
                    node 
                        fields 
                            slug
                        
                    
                
            
            projects: allMarkdownRemark 
                edges 
                    node 
                        fields 
                            slug
                        
                    
                
            
        `).then(result => 

            // In this loop we only take the articles returned by the query, and
            // pass them to the src/templates/article.js component.
            result.data.articles.edges.forEach(( node ) => 
                createPage(
                    // Rather than prepending every slug with '/article' in the
                    // createNodeField call, you can add it here to get more
                    // flexibility, and to allow different types to have different
                    // path prefixes.
                    path: `article/$node.fields.slug`,
                    component: path.resolve(`./src/templates/article.js`),
                    context: 
                        slug: node.fields.slug,
                    ,
                )
            )

            // In this loop we do the same thing, only with the projects this
            // time, and passing them to the project.js component I'm assuming
            // you have. I've prepended the path with 'project/' to separate
            // the projects from the articles.
            result.data.projects.edges.forEach(( node ) => 
                createPage(
                    path: `project/$node.fields.slug`,
                    component: path.resolve(`./src/templates/project.js`),
                    context: 
                        slug: node.fields.slug,
                    ,
                )
            )

            resolve()
        )
    )
;

我添加了一些缩进以使事情更容易推理。我可能在此过程中犯了一些语法错误,所以要小心。

我注意到在您的 onCreateNode 调用中,您将每个降价文件的 slug 设置为 'article/$slug`。如果你的项目也是 markdown 文件,你不应该这样做,因为它也会给你的项目 markdown 文件以 '/article' 开头的 slug。在上面的代码中,我在 createPage 调用中设置了正确的路径,而不是在 createNodeField 中。

您必须记住的一件事是,如果您的文章和项目都作为降价文件保存,那么您的查询将很难区分它们。我通常解决这个问题的方法是在 frontmatter 中添加一个“类型”字段。因此,在您的情况下,您将在所有文章的前端添加:type: article,并在所有项目中添加type: project。 frontmatter 部分是 Markdown 文件顶部的部分,位于三个短划线之间:

---
type: article
name: something
etc, etc...
---

您可以像这样过滤您的降价文件:

graphql(`query 
      projects: allMarkdownRemark ( filter: frontmatter:  type:  eq: "project"    ) 
        edges 
          node 
            fields 
              slug
            
          
        
      
    `);

这将只返回具有项目类型的降价文件。为您的文章做同样的事情。如果您使用 Netlify CMS,您可以添加一个隐藏字段来自动添加正确的类型来帮助您过滤。

您可能需要考虑将 Promise 更改为 async/await 调用。这是可选的,但在我看来,它使事情更容易阅读。在此处查看 gatsby 的教程 https://www.gatsbyjs.org/tutorial/part-seven/#creating-pages,了解 createPages API 中的 async/await 示例。

【讨论】:

首先循环result.data.articles.edges.forEach(( node ) => 行似乎导致TypeError: Cannot read property 'articles' of undefined... project.js 文件与article.js 位于同一模板文件夹中。我是否应该进一步更改您提供的代码以便将文章/项目帖子都称为降价?我确实希望两者都是降价文件,并且正在使用您添加“类型”frontmatter 和过滤器调用我的降价文件的方法。 这很奇怪,我在这里工作。这将是我第一个示例中的代码复制/粘贴到我的 gatsby-node.js 文件中。如果它无法读取未定义的“文章”,则意味着 result.data 未定义。您可以在开始循环遍历 result.data 之前输入console.log(result) 吗?如果那里出了问题,我很想知道result 参数包含什么样的内容。就类型变量而言,一旦我们弄清楚为什么它找不到数据属性,我将编辑我的答案以处理文件的过滤。 抱歉 - 我对 javascript 的了解有限,我不确定如何查看 result.data 是否未定义。我应该怎么做?您还想更仔细地查看其他文件吗? 我不知道你在做什么是私有的,或者你是否可以将源代码上传到 github。另外,刚刚看到这个可能有用的问题:***.com/questions/54637465/…

以上是关于在 Gatsby 上创建第二个博客模板的主要内容,如果未能解决你的问题,请参考以下文章

正确地将第二个动态模板添加到 Gatsby/NetlifyCMS - 我哪里出错了?

如何创建单个 Gatsby 页面以按标签/类别显示和过滤所有博客文章

用html页面模板使用django完成个人博客

Gatsby 和 GraphQL:访问模板页面中的数据

在 Gatsby 博客中的 url 路径中附加 %20

第二个随笔