【问题标题】:Multiple templates in Gatsby.jsGatsby.js 中的多个模板
【发布时间】:2021-02-16 23:27:16
【问题描述】:

我对 Gatsby 还很陌生,我希望有多个模板可以用于我的不同页面,例如 Landingpage、Contact 和 About 不能有相同的模板。

我现在在 gatsby-node.js 中做了一个丑陋的代码,但它只适用于两个模板,这是我的代码(顺便说一句,我从 markdownfiles 中获取数据):

Mardown 文件 1:

---
title: 'Index page'
slug: '/'
template: indexTemplate //This is where i declare which template it shall use
content: 'Index content'
---

Mardownfile 2:

---
slug: '/my-first-post'
date: '2019-05-04'
title: 'My first blog post!'
content: 'Content post 1'
template: postTemplate
---

gatsby-node.js:

exports.createPages = async ({ actions, graphql, reporter }) => {
    const { createPage } = actions
  
    const result = await graphql(`
    query{
      allMarkdownRemark {
        nodes {
          frontmatter {
            slug
            template
          }
        }
      }
    }
  `)

  const postTemplate = require.resolve(`./src/templates/postTemplate.js`)
  const indexTemplate = require.resolve(`./src/templates/indexTemplate.js`)
  const aboutTemplate = require.resolve(`./src/templates/aboutTemplate.js`) // I also want a template for my aboutpage
  
    // Handle errors
    if (result.errors) {
      reporter.panicOnBuild(`Error while running GraphQL query.`)
      return
    }

    result.data.allMarkdownRemark.nodes.forEach( post => {
      createPage({
        path: post.frontmatter.slug,
        component: post.frontmatter.template === 'indexTemplate' ? indexTemplate : postTemplate, // Here is where the ugly "magic" happens...
        context: {
          slug: post.frontmatter.slug,
        },
      })
    })
  }

【问题讨论】:

    标签: reactjs graphql markdown gatsby


    【解决方案1】:

    使用gatsby-node.js 的想法是使用createPage API 从某些数据源(CMS、markdown 文件、JSON 文件、外部 API 等)创建动态页面。换句话说,从数据源创建具有未知 slug 或名称的动态页面非常有用。这个想法是查询所有类型的数据(例如帖子)并通过上下文传递一些独特的信息(通常是 slugid)作为过滤器参数,以在您的模板中执行另一个查询。

    在您的情况下,这适合您的博客(降价文件 2),但不适用于其他已知页面,例如索引、关于等(降价文件 1)。它们是已定义的已知页面,应使用page query 区别对待。它们不是模板,因为它们是独特的页面,您不会有两个可以重复使用主页模板的不同主页。

    例如,要在帖子和主页之间识别不同的降价页面,您可以使用key 值来过滤您的查询。

    ---
    title: 'Index page'
    slug: '/'
    key: 'home'
    template: indexTemplate //This is where i declare which component it shall use
    content: 'Index content'
    ---
    Content of the index page.
    

    还有:

    ---
    slug: '/my-first-post'
    date: '2019-05-04'
    key: 'post'
    title: 'My first blog post!'
    content: 'Content post 1'
    template: postTemplate
    ---
    

    在动态创建页面时(通过gatsby-node.js),该方法应如下所示:

    const path = require(`path`)
    const { createFilePath } = require(`gatsby-source-filesystem`)
    
    exports.onCreateNode = ({ node, getNode, actions }) => {
      const { createNodeField } = actions
      if (node.internal.type === `MarkdownRemark`) {
        const slug = createFilePath({ node, getNode, basePath: `pages` })
        createNodeField({
          node,
          name: `slug`,
          value: slug,
        })
      }
    }
    
    exports.createPages = async ({ graphql, actions }) => {
      const { createPage } = actions
      const result = await graphql(`
        query {
          allMarkdownRemark(
            filter: { frontmatter: { key: { eq: "article" }}}) {
            edges {
              node {
                fields {
                  slug
                }
              }
            }
          }
        }
      `)
    
      result.data.allMarkdownRemark.edges.forEach(({ node }) => {
        createPage({
          path: node.fields.slug,
          component: path.resolve(`./src/templates/blog-post.js`),
          context: {
            // Data passed to context is available
            // in page queries as GraphQL variables.
            slug: node.fields.slug,
          },
        })
      })
    }
    

    注意key 值的过滤器。

    然后,在您的blog-post(模板)中:

    import React from "react"
    import { graphql } from "gatsby"
    import Layout from "../components/layout"
    
    export default function BlogPost({ data }) {
      const post = data.markdownRemark
      return (
        <Layout>
          <div>
            <h1>{post.frontmatter.title}</h1>
            <div dangerouslySetInnerHTML={{ __html: post.html }} />
          </div>
        </Layout>
      )
    }
    
    export const query = graphql`
      query($slug: String!) {
        markdownRemark(fields: { slug: { eq: $slug } }) {
          html
          frontmatter {
            title
          }
        }
      }
    `
    

    对于您的静态页面,只需在页面上进行查询。给你的index.js

    import React from 'react'
    import { graphql } from 'gatsby'
    
     const HomePage = ({data}) => {
      return (
        <div>
         Your page title is {data.nodes[0].frontmatter.title}
        </div>
      )
    }
    
    export const query = graphql`
      query HomePageQuery {
        allMarkdownRemark(filter: { frontmatter: { key: { eq: "home" }}}) {
           nodes{
             frontmatter{
               title
             }
           }
        }
      }
    `
    
    export default HomePage
    

    根据您的需要随意调整代码。重要的是,您应该区分应该用作模板的内容(博客、帖子、文章或其他动态数据)和应该静态处理的内容(主页、关于或其他静态页面)。


    当然,根据问题的范围,您可以有多个模板,假设一个用于帖子,另一个用于评论(或其他动态数据)。您可以通过将查询结果保存在变量中并调用两次createPage API 来简单地实现它:

    const result1 = await graphql(`
        query{
          allMarkdownRemark {
            nodes {
              frontmatter {
                slug
                template
              }
            }
          }
        }
      `)
    
    const result2 = await graphql(`
        query{
          allMarkdownRemark {
            nodes {
              frontmatter {
                slug
                template
              }
            }
          }
        }
      `)
    
      const postTemplate = require.resolve(`./src/templates/postTemplate.js`)
      const reviewsTemplate = require.resolve(`./src/templates/reviewsTemplate.js`)
        // Handle errors
        if (result1.errors || result2.errors) {
          reporter.panicOnBuild(`Error while running GraphQL query.`)
          return
        }
    
        result1.data.allMarkdownRemark.nodes.forEach( post => {
          createPage({
            path: post.frontmatter.slug,
            component: postTemplate
            context: {
              slug: post.frontmatter.slug,
            },
          })
        })
    
        result2.data.allMarkdownRemark.nodes.forEach( review => {
          createPage({
            path: review.frontmatter.slug,
            component: reviewsTemplate
            context: {
              slug: review.frontmatter.slug,
            },
          })
        })
    

    【讨论】:

    • 感谢您的回答!我回家后会更仔细地检查一下。我还有一个问题,你试过这样做吗?: result.data.allMarkdownRemark.nodes.forEach( post => { createPage({ path: post.frontmatter.slug, comoponent: path.resolve(./src/templates/${post.frontmatter.template}.js),上下文:{ slug: post.frontmatter.slug, }, }) }
    • 那你不用每次添加页面都要改gatsby-node.js?
    • 我们的想法是努力优化代码。如果你的 markdown 有组件的信息,你可以在各种页面中使用相同的循环,由组件选择应该渲染哪一个。但是,这是低可扩展性的,因为如果您的数据结构发生变化,它将迫使您更改所有页面创建。此外,它的可读性较差。当然,项目和选择是你的。重要的是要保留主页不是模板。像模板一样使用它是浪费节点周期。
    • result1result2 在最后一个示例中似乎包含相同的数据。为什么不在result1 上进行两次foreach,而不是两次查询相同的数据?
    • 这些变量是模拟显示如何从 createPages 的单个实例中的不同节点检索不同数据,因为该问题没有提供有关数据结构的更多详细信息。当然,如果真正的用例是获取相同的数据,您可以省略一个查询。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-15
    • 2022-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-06
    相关资源
    最近更新 更多