基于GraphQL在Gatsby中从两类MDX文件生成独立页面的技术求助
解决Gatsby中多文件夹MDX内容独立生成页面的问题
核心思路
通过自定义GraphQL类型+路径过滤区分不同文件夹的MDX内容,从根源避免数据冲突和页面误生成,同时为未来扩展新内容类型(如猫咪目录)预留统一的实现逻辑。
步骤1:给MDX节点标记内容类型
在gatsby-node.js中添加节点处理逻辑,根据文件所属文件夹给MDX打专属类型标签:
exports.onCreateNode = ({ node, actions, getNode }) => { const { createNodeField } = actions; // 只处理MDX类型节点 if (node.internal.type === 'Mdx') { const parentFile = getNode(node.parent); const relativePath = parentFile.relativePath; // 根据文件路径标记内容类型 if (relativePath.startsWith('blog/')) { createNodeField({ node, name: 'contentType', value: 'blog' }); } else if (relativePath.startsWith('news/')) { createNodeField({ node, name: 'contentType', value: 'news' }); } // 未来扩展猫咪目录时,添加: // else if (relativePath.startsWith('cats/')) { // createNodeField({ node, name: 'contentType', value: 'cat' }); // } } };
步骤2:编写独立的数据查询Hook
分别创建针对博客、新闻的查询Hook,只获取对应类型的内容,避免数据覆盖:
博客数据Hook(src/hooks/useBlogPosts.js)
import { useStaticQuery, graphql } from 'gatsby'; export const useBlogPosts = () => { const data = useStaticQuery(graphql` query { allMdx(filter: { fields: { contentType: { eq: "blog" } } }) { nodes { id frontmatter { header date(formatString: "YYYY-MM-DD") } fields { slug } } } } `); return data.allMdx.nodes; };
新闻数据Hook(src/hooks/useNewsPosts.js)
import { useStaticQuery, graphql } from 'gatsby'; export const useNewsPosts = () => { const data = useStaticQuery(graphql` query { allMdx(filter: { fields: { contentType: { eq: "news" } } }) { nodes { id frontmatter { title date(formatString: "YYYY-MM-DD") } fields { slug } } } } `); return data.allMdx.nodes; };
步骤3:生成独立的详情页与列表页
在gatsby-node.js中分别查询对应类型数据,生成专属页面:
exports.createPages = async ({ graphql, actions }) => { const { createPage } = actions; // 生成博客详情页 const blogData = await graphql(` query { allMdx(filter: { fields: { contentType: { eq: "blog" } } }) { nodes { fields { slug } } } } `); blogData.data.allMdx.nodes.forEach(node => { createPage({ path: `/blog/${node.fields.slug}`, component: require.resolve('./src/templates/blog-post.js'), context: { slug: node.fields.slug }, }); }); // 生成新闻详情页 const newsData = await graphql(` query { allMdx(filter: { fields: { contentType: { eq: "news" } } }) { nodes { fields { slug } } } } `); newsData.data.allMdx.nodes.forEach(node => { createPage({ path: `/news/${node.fields.slug}`, component: require.resolve('./src/templates/news-post.js'), context: { slug: node.fields.slug }, }); }); // 生成博客列表页 createPage({ path: '/blog', component: require.resolve('./src/templates/blog-list.js'), }); // 生成新闻列表页 createPage({ path: '/news', component: require.resolve('./src/templates/news-list.js'), }); };
步骤4:编写对应模板文件
博客详情页模板(src/templates/blog-post.js)
import { graphql } from 'gatsby'; import { MDXRenderer } from 'gatsby-plugin-mdx'; export const query = graphql` query($slug: String!) { mdx(fields: { contentType: { eq: "blog" }, slug: { eq: $slug } }) { frontmatter { header date(formatString: "YYYY-MM-DD") } body } } `; const BlogPost = ({ data }) => { const post = data.mdx; return ( <div> <h1>{post.frontmatter.header}</h1> <p className="date">{post.frontmatter.date}</p> <MDXRenderer>{post.body}</MDXRenderer> </div> ); }; export default BlogPost;
新闻详情页模板(src/templates/news-post.js)
import { graphql } from 'gatsby'; import { MDXRenderer } from 'gatsby-plugin-mdx'; export const query = graphql` query($slug: String!) { mdx(fields: { contentType: { eq: "news" }, slug: { eq: $slug } }) { frontmatter { title date(formatString: "YYYY-MM-DD") } body } } `; const NewsPost = ({ data }) => { const post = data.mdx; return ( <div> <h1>{post.frontmatter.title}</h1> <p className="date">{post.frontmatter.date}</p> <MDXRenderer>{post.body}</MDXRenderer> </div> ); }; export default NewsPost;
列表页示例(博客列表src/templates/blog-list.js)
import { useBlogPosts } from '../hooks/useBlogPosts'; const BlogList = () => { const posts = useBlogPosts(); return ( <div className="blog-list"> <h1>博客列表</h1> <ul> {posts.map(post => ( <li key={post.id}> <a href={`/blog/${post.fields.slug}`}>{post.frontmatter.header}</a> <span>{post.frontmatter.date}</span> </li> ))} </ul> </div> ); }; export default BlogList;
关键注意事项
- 路径匹配要准确:确保
onCreateNode中判断的relativePath和你实际的文件夹结构一致(比如博客文件是否在content/blog目录下) - 清理缓存:修改
gatsby-node.js后,必须运行gatsby clean再重新构建,避免旧数据残留 - 扩展新类型:未来添加猫咪目录等内容时,只需重复上述流程:添加类型标记、编写Hook、生成页面、创建模板即可
内容的提问来源于stack exchange,提问作者Tecnika
相关产品推荐
相关产品推荐

