如何在 Gatsby.js 中创建多个模板

how to create multiple templates in Gatsby.js

我试图为我的项目详细信息和博客 post 页面制作两个模板,但由于我在 Graphql 和 Gatsby 方面的经验不足,我设法让它适用于我的项目、项目详细信息和博客 posts 但我有一个在我的博客 posts 文章模板上找不到的错误导致它无法正常工作??

这是我的 .md 封面

---
title: The Kimo-Coffee
stack: HTML & CSS
slug: the-kimo-coffee
date: 2022/01/03
key: projects
thumb: ../images/thumbs/coffee.png
featuredImg: ../images/featured/coffee-banner.png
---

这是我的博客页面代码(工作正常)

import { graphql, Link } from "gatsby"
import React from "react"
import Layout from "../../components/Layout"
import * as styles from "../../styles/blog.module.css"
import { GatsbyImage, getImage } from "gatsby-plugin-image"

const posts = ({ data }) => {
  const posts = data.articles.nodes
  return (
    <Layout>
      <div className={styles.blog}>
        <h2>Blog Posts</h2>

        {posts.map(post => (
          <Link to={"/blog/" + post.frontmatter.slug} key={post.id}>
            <div className={styles.posts}>
              <GatsbyImage
                image={getImage(post.frontmatter.thumb)}
                alt={post.frontmatter.title}
              />
              <h3 className={styles.title}>{post.frontmatter.title}</h3>
              <p className={styles.date}>{post.frontmatter.date}</p>
              <p className={styles.stack}>{post.frontmatter.stack}</p>
              <p className={styles.description}>
                {post.frontmatter.description}
              </p>
            </div>
          </Link>
        ))}
      </div>
    </Layout>
  )
}

export default posts

export const query = graphql`
  query BlogPosts {
    articles: allMdx(
      filter: { frontmatter: { key: { eq: "article" } } }
      sort: { fields: frontmatter___date, order: DESC }
    ) {
      nodes {
        excerpt
        slug
        frontmatter {
          key
          date
          title
          stack
          description
          thumb {
            childImageSharp {
              gatsbyImageData(placeholder: BLURRED, layout: FULL_WIDTH)
            }
          }
        }
        id
      }
    }
  }
`

这是我的博客 post 详细信息(不起作用)

import React from "react"
import { graphql, Link } from "gatsby"
import { MDXRenderer } from "gatsby-plugin-mdx"
import { GatsbyImage, getImage } from "gatsby-plugin-image"
import Layout from "../components/Layout"
import * as styles from "../styles/blog-details.module.css"

const blogPosts = ({ data, pageContext }) => {
  const { body } = data.article
  const { title, date, stack, featuredImg } = data.article.frontmatter
  const { previous, next } = pageContext

  return (
    <Layout>
      <div className={styles.details}>
        <h2>{title}</h2>
        <p>{date}</p>
        <p>{stack}</p>
        <div className={styles.featured}>
          <GatsbyImage
            image={getImage(featuredImg.childImageSharp.gatsbyImageData)}
            alt={title}
          />
        </div>
        <article className={styles.body}>
          <MDXRenderer>{body}</MDXRenderer>
        </article>
        <React.Fragment>
          {previous && (
            <Link to={previous.frontmatter.slug}>
              <button>{previous.frontmatter.title}</button>
            </Link>
          )}
        </React.Fragment>
        <React.Fragment>
          {next && (
            <Link to={next.frontmatter.slug}>
              <button>{next.frontmatter.title}</button>
            </Link>
          )}
        </React.Fragment>
      </div>
    </Layout>
  )
}

export default blogPosts

export const query = graphql`
  query BlogDetails($slug: String) {
    article: mdx(frontmatter: { slug: { eq: $slug }, key: { eq: "article" } }) {
      body
      frontmatter {
        slug
        key
        title
        date
        stack
        featuredImg {
          childImageSharp {
            gatsbyImageData(placeholder: BLURRED, layout: FULL_WIDTH)
          }
        }
      }
    }
  }
`

最后这是我的 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 === `Mdx`) {
    const slug = createFilePath({ node, getNode, basePath: `pages` })
    createNodeField({
      node,
      name: `slug`,
      value: slug,
    })
  }
}

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

  const results1 = await graphql(`
    query content {
      allMdx(filter: { frontmatter: { key: { eq: "projects" } } }) {
        nodes {
          frontmatter {
            slug
            title
          }
        }
      }
    }
  `)

  const results2 = await graphql(`
    query content {
      allMdx(filter: { frontmatter: { key: { eq: "article" } } }) {
        nodes {
          frontmatter {
            slug
            title
          }
        }
      }
    }
  `)

  const projectTemplate = path.resolve(`src/templates/project-details.js`)
  const blogTemplate = path.resolve(`src/templates/blog-details.js`)
  if (results1.errors || results2.errors) {
    reporter.panicOnBuild(`Error while running GraphQL query.`)
    return
  }

  const posts = results2.data.allMdx.nodes

  posts.forEach((post, index) => {
    const previous = index === post.length - 1 ? null : post[index + 1]
    const next = index === 0 ? null : posts[index - 1]
    createPage({
      path: `/blog/${post.frontmatter.slug}`,
      component: blogTemplate,
      context: {
        slug: post.frontmatter.slug,
        previous,
        next,
      },
    })
  })

  results1.data.allMdx.nodes.forEach(node => {
    createPage({
      path: `/projects/${node.frontmatter.slug}`,
      component: projectTemplate,
      context: { slug: node.frontmatter.slug },
    })
  })
}

抱歉,如果我的代码很丑陋,我是编程的新手,我从 Gatsby 开始,因为它让我在学习 javascript 并做出反应后感到乐观 :)

你的代码中有一些奇怪的地方,尽管你说它可以工作,但它们不应该工作。

在 React 中,所有组件都必须大写,否则,React 会将它们解释为 HTML 元素,并且由于组件名称通常不匹配 HTML 标签,因此会中断,因为它不会存在。

在您的情况下,两个模板都必须重命名为:

const Posts = ({ data }) => {}

并且:

const BlogPosts = ({ data, pageContext }) => {}

提示错误:

"There's not a page or function yet at /blog/undefined"

这意味着模板无效,因为没有有效的函数模板。一旦大写,问题应该就消失了。

此外,double-check 降价以确保您在所有这些中都有一个 slug 字段。

您也可以输入 localhost:8000/404,它正在开发中,显示所有生成页面的列表。您可以在那里检查是否正确创建了所有帖子。如果是这样,则问题出在您的模板中。否则,它在您的页面生成中。

您的问题出在 BlogPosts 的 GraphQL 查询中,其中文章的 link 设置不正确。您已定义如下查询:

  query BlogPosts {
    articles: allMdx(
      filter: { frontmatter: { key: { eq: "article" } } }
      sort: { fields: frontmatter___date, order: DESC }
    ) {
      nodes {
        excerpt
        slug # <-- here's the issue
        frontmatter {
          key
          date
          title
          stack
          description
          thumb {
            childImageSharp {
              gatsbyImageData(placeholder: BLURRED, layout: FULL_WIDTH)
            }
          }
        }
        id
      }
    }
  }

其中 slug 直接位于 nodes 节点下。同时,您正在 linking 鼻涕虫,因为它是 frontmatter 的 child at:

<Link to={"/blog/" + post.frontmatter.slug} key={post.id}>

这导致 blog/undefined 路径。您可以检查悬停在 link 上的路径,看看它指向哪里。

根据您的数据结构,确保它应该是 slug 字段。我会说它应该在 frontmatter:

        frontmatter {
          slug # <-- add it here
          key
          date
          title
          stack
          description
          thumb {
            childImageSharp {
              gatsbyImageData(placeholder: BLURRED, layout: FULL_WIDTH)
            }
          }
        }

或者,只需更改 Link 结构,如:

<Link to={"/blog/" + post.slug} key={post.id}>

TL:DR

因为:

createNodeField({
  node,
  name: `slug`,
  value: slug,
})

您正在根据文件名创建一个 slug,它作为 node child 插入,这就是查询有效但对它的引用无效的原因.