使用Next.js和Firebase读取单文档时持续出现序列化错误
问题分析与解决方案
首先看报错核心:undefined 无法被序列化为JSON,这是因为返回给客户端的post值是undefined,同时还有Firestore Timestamp对象序列化的问题。下面是具体问题点和修复步骤:
问题1:getServerSideProps未传递slug参数
你的getServerSideProps调用getPostBySlug()时没有传入URL中的slug,导致查询的文档不存在,最终post为undefined。
问题2:文档不存在时返回undefined
getPostBySlug在文档不存在时没有明确返回值(默认是undefined),而Next.js不允许将undefined作为props传递给组件。
问题3:Firestore Timestamp未序列化
直接返回的docSnap.data()中,date是Firestore的Timestamp对象,无法被JSON序列化,需要转换为时间戳或字符串。
修复步骤
1. 修复getServerSideProps,传递slug参数并处理空值
修改slug页面的getServerSideProps,从上下文获取slug参数,并处理空值情况:
export async function getServerSideProps(context) { // 获取URL中的slug(Next.js 12及以下用context.query.slug,13+ App Router用context.params.slug) const slug = context.params?.slug || context.query.slug; // 如果slug为空,直接返回404 if (!slug) { return { notFound: true }; } const post = await getPostBySlug(slug); // 文档不存在时返回404 if (!post) { return { notFound: true }; } return { props: { post }, }; }
2. 完善getPostBySlug函数,序列化Timestamp并返回null
修改Firebase代码中的getPostBySlug,确保返回可序列化的数据,且文档不存在时返回null:
export const getPostBySlug = async (slug) => { const docRef = doc(db, "posts", slug); const docSnap = await getDoc(docRef); if (docSnap.exists()) { const postData = docSnap.data(); // 转换Timestamp为时间戳,和getPosts保持一致 return { ...postData, id: docSnap.id, date: postData.date?.toDate().getTime(), }; } else { console.log("No Such Document"); // 返回null而非undefined return null; } };
3. 组件中处理post为空的情况
修改PostPage组件,确保在post不存在时显示友好提示:
export default function PostPage({ post }) { // 这里其实getServerSideProps已经处理了404,额外做一层兜底 if (!post) { return ( <div className="post-not-found"> <h1>文章不存在</h1> <p>你访问的文章可能已被删除或从未存在</p> </div> ); } return ( <div className="post"> <h1>{post.title}</h1> <span>Published {moment(post.date).format("LL")}</span> <p dangerouslySetInnerHTML={{ __html: post.content }}></p> </div> ); }
内容的提问来源于stack exchange,提问作者flashjordan
相关产品推荐
相关产品推荐

