NextJS getServerSideProps仅单篇文章可用,其余页面冻结求助
问题描述
我用NextJS TypeScript + MongoDB搭建博客,通过getServerSideProps给动态路由[slug].tsx页面获取数据。现在遇到个问题:只有某一篇文章能正常渲染,其他文章页面会卡顿甚至冻结。但通过useEffect能确认服务端已经把props传过来,并且成功设置到post和author状态里了。
因为内容需要定期修改,所以不会用getStaticProps。另外各文章之间没有关键差异。
代码实现
export const getServerSideProps: GetServerSideProps = async ({params}: any) => { const clientPromise = await getClient() const post = await clientPromise .db("my-db") .collection("articles") .findOne({ 'titleSlug': params.slug }, {projection: { "_id": 0 }}) if (post) { const author = await clientPromise .db("my-db") .collection("auteurs") .findOne({ 'slug': post.authorSlug }, {projection: { "_id": 0 }}) await clientPromise.close() return { props: { post: post, author: author}} } await clientPromise.close() return { props: { error: true }} } function Post (props: InferGetServerSidePropsType<typeof getServerSideProps>) { const [post, setPost] = React.useState<any>(null) const [author, setAuthor] = React.useState<any>(null) React.useEffect(() => { console.log(props) if (props.post) { setPost(props.post) } if (props.author ) { setAuthor(props.author) } }, [props]) if (!props || !post || !author) { return null } return ( <> <main className={"main"}> <Container maxWidth={"md"} sx={{ py: 4 }}> <PostView post={post} author={author} /> </Container> </main> </> ) } export default Post
依赖版本
{ "@types/node": "18.15.9", "@types/react": "18.0.29", "@types/react-dom": "18.0.11", "eslint": "8.36.0", "eslint-config-next": "13.2.4", "mongodb": "^4.8.0", "next": "13.2.4", "react": "18.2.0", "react-dom": "18.2.0", "typescript": "5.0.2" }
解决方案
问题根源
- MongoDB客户端频繁关闭:手动调用
clientPromise.close()会破坏MongoDB的连接池复用机制,每次请求都重新创建/关闭连接,这是性能卡顿的核心原因。 - 多余的本地状态:服务端已经把数据通过props传递到组件,没必要用
useState+useEffect二次转存,这会引发额外渲染循环,导致状态不同步。 - 初始返回null:页面初始化时直接返回null,会导致DOM空白后突然渲染,加重卡顿感。
修复后的代码
1. 修改getServerSideProps(保留连接池复用)
import { GetServerSideProps } from 'next'; import { InferGetServerSidePropsType } from 'next'; import getClient from './path-to-your-getClient'; // 替换为你的getClient路径 export const getServerSideProps: GetServerSideProps = async ({ params }) => { const client = await getClient(); const post = await client .db("my-db") .collection("articles") .findOne({ 'titleSlug': params?.slug }, { projection: { "_id": 0 } }); if (!post) { return { props: { error: true } }; } const author = await client .db("my-db") .collection("auteurs") .findOne({ 'slug': post.authorSlug }, { projection: { "_id": 0 } }); // 不要手动关闭客户端,MongoDB连接池会自动管理连接 return { props: { post, author } }; };
2. 简化组件(直接使用props)
import React from 'react'; import { Container } from '@mui/material'; import PostView from './path-to-your-PostView'; // 替换为你的PostView路径 function Post(props: InferGetServerSidePropsType<typeof getServerSideProps>) { if (props.error) { return <div>文章不存在</div>; } // 用加载提示替代null返回,避免页面空白闪烁 if (!props.post || !props.author) { return <div>加载中...</div>; } return ( <> <main className="main"> <Container maxWidth="md" sx={{ py: 4 }}> <PostView post={props.post} author={props.author} /> </Container> </main> </> ); } export default Post;
3. 确保getClient是单例(连接池复用关键)
你的getClient应该返回单例客户端实例,避免每次请求创建新连接:
import { MongoClient } from 'mongodb'; const uri = process.env.MONGODB_URI!; let client: MongoClient; let clientPromise: Promise<MongoClient>; if (!process.env.MONGODB_URI) { throw new Error('请在.env文件中配置MONGODB_URI'); } // 开发环境下避免热重载重复创建客户端 if (process.env.NODE_ENV === 'development') { if (!global._mongoClientPromise) { client = new MongoClient(uri); global._mongoClientPromise = client.connect(); } clientPromise = global._mongoClientPromise; } else { client = new MongoClient(uri); clientPromise = client.connect(); } export default clientPromise;
内容的提问来源于stack exchange,提问作者Horkos
相关产品推荐
相关产品推荐

