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

如何解决Next.js预渲染中的“Cannot read property 'title' of undefined”错误?

解决Next.js预渲染时Cannot read property 'title' of undefined错误

我来帮你拆解这个问题,其实根源并不是<Head>组件的位置,而是预渲染阶段postData可能为undefined,但你的代码直接访问了postData.title。

错误原因拆解

当你运行npm run build时,Next.js会预渲染getStaticPaths返回的所有路径。如果某个slug对应的文章查询失败(比如slug无效、Apollo查询返回空),getStaticProps会返回postData: undefined,这时候组件渲染到<Head>里的{postData.title}时,就会抛出"Cannot read property 'title' of undefined"的错误。而dev模式下因为fallback: true,未预渲染的路径会先显示Loading,不会立刻触发这个问题。

具体修复方案

1. 在getStaticProps中处理无效请求,返回404

如果查询不到对应slug的文章,直接告诉Next.js返回404页面,避免后续渲染出错:

export async function getStaticProps({ params }) {
  const { data } = await client.query({
    query: POST,
    variables: { id: params.slug, idType: 'SLUG' }
  });

  // 若未找到文章,返回404
  if (!data?.post) {
    return {
      notFound: true,
    };
  }

  return { props: { postData: data.post } };
}

2. 给组件中的postData增加安全判断

即使有了上面的处理,在fallback: true的场景下(用户访问未预渲染的路径时),组件会先渲染Loading状态,此时postData还是undefined,所以需要给<Head>和其他依赖postData的地方加条件判断:

export default function Post({ postData }) {
  const router = useRouter();
  
  // 处理fallback或数据不存在的情况
  if (!router.isFallback && !postData) {
    return <p>Hmm... looks like this post doesn't exist</p>
  }

  const formatDate = date => {
    const newDate = new Date(date);
    return `${newDate.getDate()}/${ newDate.getMonth() + 1 }/${newDate.getFullYear()}`
  };

  return (
    <div className={styles.container}>
      <Head>
        {/* 安全获取标题,fallback时显示加载中 */}
        <title>{postData ? postData.title : 'Loading...'}</title>
        <link rel='icon' href='/favicon.ico' />
      </Head>
      <main className={styles.main}>
        {router.isFallback ? (
          <h2>Loading...</h2>
        ) : (
          <article className={blogStyles.article}>
            <div className={blogStyles.postmeta}>
              <h1 className={styles.title}>{postData.title}</h1>
              <p>{formatDate(postData.date)}</p>
              <img src={postData.featuredImage.node.sourceUrl} />
            </div>
            <div className='post-content content' dangerouslySetInnerHTML={{ __html: postData.content }} />
          </article>
        )}
        <p>
          <Link href={`/blog`}>
            <a>Back</a>
          </Link>
        </p>
      </main>
    </div>
  )
}

3. 修正getStaticPaths中的无效路径生成逻辑

你原来的条件判断data?.posts.edges.node !== null有问题,因为edges是数组,应该过滤掉没有有效slug的节点,避免生成无效的预渲染路径:

export async function getStaticPaths() {
  const { data } = await client.query({ query: POSTS_WITH_SLUGS });
  
  // 过滤出有有效slug的文章
  const validPosts = data?.posts?.edges?.filter(edge => edge?.node?.slug) || [];

  return {
    paths: validPosts.map(({ node }) => `/blog/${node.slug}`),
    fallback: true
  };
}

关于<Head>组件位置的疑问

完全不需要把<Head>移到/pages目录之外,它的位置是正确的。问题的核心是没有处理postData为undefined的边界情况,和组件位置无关。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 13:27:47