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

Gatsby+Contentful技术求助:如何将文章列表展示在首页而非单独动态页面(已遇TypeError报错)

解决方案:两种方式实现首页展示Contentful文章列表

你遇到的核心问题主要有两个:一是直接导入news-list.js时,组件依赖的分页查询参数(skip/limit)没有传递导致报错;二是gatsby-node.js中创建的首页路径被src/pages/index.js覆盖,所以配置没生效。下面给你两种可行的解决思路:


方法一:直接在首页组件(index.js)中查询并渲染文章列表

这种方式最简单,不需要依赖gatsby-node.js的分页配置,直接在首页写查询获取文章数据。你当前的index.js已经有雏形,只需要修正几个小问题:

  1. 修正Link跳转路径:你在gatsby-node.js中创建的文章详情页路径是/news/${node.slug},所以Link的to属性要对应这个路径,而不是直接写node.slug。
  2. 合并GraphQL查询:不需要同时使用页面查询(page query)和useStaticQuery,可以把站点元数据和文章数据合并到一个查询里,代码更简洁。

修正后的index.js代码如下:

import React from 'react';
import { graphql, Link } from 'gatsby';
import Layout from '../components/layout';
import SEO from '../components/seo';

// 合并站点元数据和文章数据的查询
export const query = graphql`
  query {
    site {
      siteMetadata {
        companyname
      }
    }
    allContentfulPost(sort: { fields: date, order: DESC }) {
      edges {
        node {
          title
          slug
          date(formatString: "MMMM Do, YYYY")
        }
      }
    }
  }
`;

const Index = ({ data }) => {
  const { companyname } = data.site.siteMetadata;
  const posts = data.allContentfulPost.edges;

  return (
    <Layout>
      <SEO title="Home" />
      <section className='c-mt-10'>
        <div className=''>
          <div className='font-mono md:flex md:justify-between'>
            <div className='mb-5'>
              <a href={`mailto:hello@${companyname}.co.uk`}>
                hello@pfb{companyname}.co.uk
              </a>
              <br />
              <br />
              <tel>+44 020 3925 6054</tel>
            </div>
            <a href='https://www.google.com/maps/place/Warnford+Court,+29+Throgmorton+St,+London+EC2N+2AT/@51.5154096,-0.0890419,17z/data=!3m1!4b1!4m5!3m4!1s0x48761cacb440b98d:0x9742679143333ff!8m2!3d51.5154096!4d-0.0868479' target='_blank' rel='noreferrer'>
              <address className='text-right'>
                Warnford Court
                <br />
                29 Throgmorton Street
                <br />
                London, EC2N 2AT
              </address>
            </a>
          </div>
        </div>
        <div>
          <h2>Company News</h2>
          <ul>
            {posts.map(({ node }) => (
              <li key={node.slug}>
                <Link to={`/news/${node.slug}`}>{node.title}</Link>
                <span className='block text-sm text-gray-500'>{node.date}</span>
              </li>
            ))}
          </ul>
        </div>
      </section>
    </Layout>
  );
};

export default Index;

方法二:让首页复用news-list.js组件(保留分页功能)

如果你想保留分页逻辑,让首页作为文章列表的第一页,需要解决gatsby-node.js创建的首页被src/pages/index.js覆盖的问题:

  1. 删除或重命名src/pages/index.js:Gatsby会优先使用src/pages下的文件创建路由,所以如果存在index.js,gatsby-node.js中path: '/'的页面不会生效。你可以把原index.js的内容整合到news-list.js,或者把index.js重命名为其他名称(比如home-content.js),作为组件导入到news-list.js中。
  2. 调整news-list.js,添加首页的额外内容:把原首页的联系信息等内容加到news-list.js里,或者做成组件复用。
  3. 确保gatsby-node.js的分页配置正确:你的分页代码已经正确设置了第一页路径为/,只要没有src/pages/index.js冲突,这个配置就会生效。

调整后的news-list.js示例(整合首页内容):

import React from 'react';
import { Link, graphql, useStaticQuery } from 'gatsby';
import Layout from '../components/layout';
import SEO from '../components/seo';

export const query = graphql`
  query ($skip: Int!, $limit: Int!) {
    allContentfulPost(sort: { fields: date, order: DESC }, limit: $limit, skip: $skip) {
      edges {
        node {
          title
          slug
          date(formatString: "MMMM Do, YYYY")
        }
      }
    }
  }
`;

const NewsList = (props) => {
  // 获取站点元数据
  const { site } = useStaticQuery(
    graphql`
      query {
        site {
          siteMetadata {
            companyname
          }
        }
      }
    `
  );
  const { companyname } = site.siteMetadata;
  const posts = props.data.allContentfulPost.edges;
  const { currentPage, postNumPages } = props.pageContext;

  return (
    <Layout>
      <SEO title={currentPage === 1 ? "Home" : `News - Page ${currentPage}`} />
      {/* 原首页的联系信息 */}
      <section className='c-mt-10'>
        <div className=''>
          <div className='font-mono md:flex md:justify-between'>
            <div className='mb-5'>
              <a href={`mailto:hello@${companyname}.co.uk`}>
                hello@pfb{companyname}.co.uk
              </a>
              <br />
              <br />
              <tel>+44 020 3925 6054</tel>
            </div>
            <a href='https://www.google.com/maps/place/Warnford+Court,+29+Throgmorton+St,+London+EC2N+2AT/@51.5154096,-0.0890419,17z/data=!3m1!4b1!4m5!3m4!1s0x48761cacb440b98d:0x9742679143333ff!8m2!3d51.5154096!4d-0.0868479' target='_blank' rel='noreferrer'>
              <address className='text-right'>
                Warnford Court
                <br />
                29 Throgmorton Street
                <br />
                London, EC2N 2AT
              </address>
            </a>
          </div>
        </div>
        {/* 文章列表 */}
        <div className='mt-8'>
          <h2>Company News</h2>
          <div className='container mx-auto prose prose-lg'>
            {posts.map(({ node }) => {
              const title = node.title || node.slug;
              return (
                <div className='mb-2' key={node.slug}>
                  <Link to={`/news/${node.slug}`}>
                    <h3 className='underline font-sans mb-1'>{title}</h3>
                  </Link>
                  <div className='flex items-center justify-between'>
                    <span className='font-mono text-sm'>{node.date}</span>
                  </div>
                </div>
              );
            })}
          </div>
          {/* 分页导航(如果需要) */}
          <div className='mt-6 flex justify-between'>
            {currentPage > 1 && (
              <Link to={currentPage === 2 ? '/' : `/news/${currentPage - 1}`}>
                ← Previous Page
              </Link>
            )}
            {currentPage < postNumPages && (
              <Link to={`/news/${currentPage + 1}`}>
                Next Page →
              </Link>
            )}
          </div>
        </div>
      </section>
    </Layout>
  );
};

export default NewsList;

之后,删除src/pages/index.js,重新启动Gatsby开发服务器,首页/就会显示带分页的文章列表,同时保留原首页的联系信息。


关键注意事项

  • 路由优先级:Gatsby中src/pages下的文件会自动生成路由,优先级高于gatsby-node.js创建的页面,所以如果要让gatsby-node创建的首页生效,必须移除src/pages/index.js。
  • Link路径匹配:确保文章详情页的Link路径和gatsby-node.js中createPage的path完全一致,否则会出现404错误。
  • GraphQL查询类型:页面组件可以使用page query(export const query),而普通组件只能使用useStaticQuery或StaticQuery,这也是你之前直接导入news-list.js报错的原因——news-list依赖page query的参数,直接导入时没有传递pageContext和data。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 23:33:08