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

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"
}
解决方案

问题根源

  1. MongoDB客户端频繁关闭:手动调用clientPromise.close()会破坏MongoDB的连接池复用机制,每次请求都重新创建/关闭连接,这是性能卡顿的核心原因。
  2. 多余的本地状态:服务端已经把数据通过props传递到组件,没必要用useState+useEffect二次转存,这会引发额外渲染循环,导致状态不同步。
  3. 初始返回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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 19:15:09