You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Gatsby多内容类型配置求助:实现Markdown多模板与Frontmatter适配

嘿,我之前正好折腾过Gatsby里多内容类型+独立模板的场景,其实没你想的那么复杂,咱们一步步捋清楚:

第一步:整理内容目录结构

先把不同类型的Markdown文件分开存放,这样后续好区分。比如在项目根目录建个content文件夹,下面再拆两个子目录:

  • content/blog:专门放你的BlogPost类型Markdown文件,比如你给的示例:

title: My Post
date: "2017-09-21"

This is my blog body

  • content/projects:放Project类型的Markdown文件,比如可以加自己需要的Frontmatter字段(比如techStack、demoUrl之类的)

第二步:配置文件系统源插件

在gatsby-config.js里,给gatsby-source-filesystem插件配置两个实例,分别读取上面的两个目录,重点是给每个实例加name字段,这个字段是后续区分内容类型的关键:

module.exports = {
  plugins: [
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `blog`, // 标记这是博客内容
        path: `${__dirname}/content/blog/`,
      },
    },
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `projects`, // 标记这是项目内容
        path: `${__dirname}/content/projects/`,
      },
    },
    `gatsby-transformer-remark`, // 别忘了加这个解析Markdown的插件
  ],
}

第三步:生成Slug并创建页面

接下来在gatsby-node.js里做两件事:一是给每个Markdown文件生成访问路径(slug),二是根据内容类型匹配对应的模板并创建页面。

首先加生成slug的逻辑:

const { createFilePath } = require(`gatsby-source-filesystem`)

exports.onCreateNode = ({ node, getNode, actions }) => {
  const { createNodeField } = actions
  // 只处理Markdown文件
  if (node.internal.type === `MarkdownRemark`) {
    // 从文件路径生成slug
    const slug = createFilePath({ node, getNode, basePath: `content` })
    createNodeField({
      node,
      name: `slug`,
      value: slug,
    })
  }
}

然后加创建页面的逻辑,通过sourceInstanceName(就是刚才配置的name字段)判断内容类型,分配对应模板:

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

  // 查询所有Markdown内容
  const queryResult = await graphql(`
    query AllContentQuery {
      allMarkdownRemark {
        nodes {
          id
          fields {
            slug
          }
          sourceInstanceName # 拿到之前标记的内容类型
        }
      }
    }
  `)

  // 遍历每个内容节点,创建对应页面
  queryResult.data.allMarkdownRemark.nodes.forEach(node => {
    let templatePath
    // 根据类型选择模板
    if (node.sourceInstanceName === `blog`) {
      templatePath = `./src/templates/blog-post.js`
    } else if (node.sourceInstanceName === `projects`) {
      templatePath = `./src/templates/project.js`
    }

    createPage({
      path: node.fields.slug, // 页面访问路径
      component: require.resolve(templatePath), // 对应的模板文件
      context: {
        id: node.id, // 把节点ID传给模板,方便后续查询内容
      },
    })
  })
}

第四步:创建独立模板文件

现在分别写两个模板,每个模板对应自己的内容字段。

比如src/templates/blog-post.js,专门渲染BlogPost内容:

import React from "react"
import { graphql } from "gatsby"

export default function BlogPostTemplate({ data }) {
  const post = data.markdownRemark
  return (
    <div className="blog-post">
      <h1>{post.frontmatter.title}</h1>
      <p className="post-date">{post.frontmatter.date}</p>
      {/* 渲染Markdown正文 */}
      <div dangerouslySetInnerHTML={{ __html: post.html }} />
    </div>
  )
}

// 通过ID查询对应的博客内容
export const query = graphql`
  query BlogPostQuery($id: String!) {
    markdownRemark(id: { eq: $id }) {
      html
      frontmatter {
        title
        date
      }
    }
  }
`

再写src/templates/project.js,适配Project的Frontmatter字段(比如假设Project有title、techStack、description):

import React from "react"
import { graphql } from "gatsby"

export default function ProjectTemplate({ data }) {
  const project = data.markdownRemark
  return (
    <div className="project">
      <h1>{project.frontmatter.title}</h1>
      <div className="tech-stack">
        <span>技术栈:</span>
        {project.frontmatter.techStack.join(", ")}
      </div>
      <div className="project-description">
        <p>{project.frontmatter.description}</p>
      </div>
      <div dangerouslySetInnerHTML={{ __html: project.html }} />
    </div>
  )
}

export const query = graphql`
  query ProjectQuery($id: String!) {
    markdownRemark(id: { eq: $id }) {
      html
      frontmatter {
        title
        techStack
        description
      }
    }
  }
`

最后测试一下

把你的Markdown文件放到对应的目录,运行gatsby develop,访问生成的slug路径(比如/blog/my-post/),就能看到不同类型的内容用各自的模板渲染啦!

如果不想用目录区分类型,也可以在每个Markdown的Frontmatter里加type: "blog"或者type: "project",然后在GraphQL查询里用frontmatter.type来判断,不过用目录的方式更直观,也方便管理。

内容的提问来源于stack exchange,提问作者dvreed77

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 04:26:29