Gatsby博客相关文章加载后重复显示问题求助
Gatsby相关文章区块加载后重复显示问题修复
问题描述
基于Gatsby搭建的博客,文章页的相关文章区块初始显示正常,但页面加载约30秒后,部分或全部相关文章出现重复。推测当某篇文章与当前文章匹配多个标签时,会在列表中重复显示(例如当前文章带有tag-1、tag-2、tag-3,另一篇文章同时带有tag-1和tag-2,就会重复出现在相关列表中)。
相关代码
related.js组件
import React from "react"; import { Link, StaticQuery, graphql } from "gatsby"; const RelatedArticles = ({ posts }) => { return ( <div> {posts.map(({ node }) => { const title = node.frontmatter.title; return ( <div key={node.fields.slug}> <div> <Link to={`/blog${node.fields.slug}`}> {title} </Link> </div> <small>{node.frontmatter.date}</small> </div> ); })} </div> ); }; export const relatedArticlesQuery = graphql` query { allMdx(sort: { fields: [frontmatter___date], order: DESC }) { edges { node { excerpt fields { slug } frontmatter { date(formatString: "MMMM DD, YYYY") title tags } } } } } `; const RelatedPosts = (props) => { const related = []; return ( <StaticQuery query={relatedArticlesQuery} render={(data) => { const { allMdx: { edges }, } = data; // iterate through article vertex for (const vertex of edges) { // handle base case if (related.length >= props.limit) { continue; } // abstract tags const { frontmatter: { tags }, } = vertex.node; // handle case where there are no tags or prop tags if (!tags || !props.tags) { continue; } const formattedTags = tags // ensure tags match and article is not the same as current if ( props.tags.some((item) => formattedTags.includes(item)) && props.title !== vertex.node.frontmatter.title ) { related.push(vertex); } } // render posts return ( <div className="related"> <h3> Вам может понравиться </h3> <RelatedArticles posts={related} /> </div> ); }} /> ); }; export default RelatedPosts
博客文章模板代码
const BlogPost = ({ data , children }) => { const image = getImage(data.mdx.frontmatter.featuredImage) const tags = data.mdx.frontmatter.tags const categories = data.mdx.frontmatter.categories return ( <Layout pageTitle={data.mdx.frontmatter.title}> <ul>{tags && tags .map(tag => tag.trim()) .filter(tag => tag.length > 0) .map(tag => <li key={tag}><Link to={`/blog/tag/${_.kebabCase(transliterate(tag))}`}>{tag}</Link></li>)} </ul> <ul>{categories && categories .map(category => category.trim()) .filter(category => category.length > 0) .map(category => <li key={category}><Link to={`/blog/category/${_.kebabCase(transliterate(category))}`}>{category}</Link></li>)} </ul> <p>{data.mdx.frontmatter.date}</p> <GatsbyImage image={image} /> {children} <RelatedPosts tags={data.mdx.frontmatter.tags} limit={6} title={data.mdx.frontmatter.title} /> </Layout> ) } export const query = graphql` query ($id: String) { mdx(id: {eq: $id}) { frontmatter { title date(formatString: "DD MMMM YYYY") featuredImage { childImageSharp { gatsbyImageData } } tags categories type } excerpt body } } ` export const Head = ({ data }) => ( <Seo title={data.mdx.frontmatter.title} description={data.mdx.excerpt} /> ) export default BlogPost
问题分析
- 数组作用域错误:
related数组定义在StaticQuery的render回调外部,当render回调被多次执行(比如Gatsby的服务器端渲染+客户端hydration,或者数据更新),数组不会被重置,会持续累加符合条件的文章,导致重复。 - 缺少去重逻辑:即使单篇文章因多标签匹配被多次遍历(原逻辑中不会,但如果数据异常或逻辑调整可能出现),没有机制阻止重复添加同一篇文章。
修复方案
修改RelatedPosts组件,将related数组移到render回调内部,并添加基于文章slug的去重逻辑:
const RelatedPosts = (props) => { return ( <StaticQuery query={relatedArticlesQuery} render={(data) => { const { allMdx: { edges } } = data; const related = []; const addedSlugs = new Set(); // 用于记录已添加的文章slug,避免重复 for (const vertex of edges) { if (related.length >= props.limit) { break; // 达到数量限制后直接退出循环,比continue更高效 } const { node } = vertex; const { tags, title } = node.frontmatter; const slug = node.fields.slug; if (!tags || !props.tags) { continue; } // 跳过当前文章,以及已经添加过的文章 if (title === props.title || addedSlugs.has(slug)) { continue; } // 检查是否有匹配的标签 const hasMatchingTag = props.tags.some(item => tags.includes(item)); if (hasMatchingTag) { related.push(vertex); addedSlugs.add(slug); } } return ( <div className="related"> <h3>Вам может понравиться</h3> <RelatedArticles posts={related} /> </div> ); }} /> ); };
修复要点
- 将
related数组和addedSlugs集合移到render回调内部,确保每次回调执行时都从空开始,避免累加。 - 使用
Set记录已添加的文章slug,彻底阻止同一篇文章被重复添加,无论是否因多标签匹配或回调多次执行。 - 将
continue改为break,当达到limit数量时直接退出循环,提升性能。 - 简化变量提取逻辑,让代码更清晰。
内容的提问来源于stack exchange,提问作者Лео
相关产品推荐
相关产品推荐

