如何解决Next.js预渲染中的“Cannot read property 'title' of undefined”错误?
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

