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

Next.js 13 + Prisma ORM数据获取问题:导航时显示旧数据

Next.js 13 服务器组件数据缓存问题解决

我使用Next.js 13和Prisma ORM在服务器组件中获取数据,通过上下文提供给子组件,路由结构为/item/[id],在layout.tsx中使用id获取数据。初始加载时数据正常传递,但修改数据(通过API更新数据库)后,导航到新页面再返回时,仍显示初始加载的旧数据。服务端函数getChildItems本应每次页面加载时执行,但仅在页面刷新时运行,而非前后导航时。我可以在数据变更时使用next/navigation的router.refresh(),但认为这并非最优方案。由于是服务器组件,无法使用useEffect这类钩子;虽可在客户端组件从API取数,但希望尽可能保持服务端渲染。

我的layout.tsx代码

import { Item } from "@prisma/client";
import { ListProvider } from "~/context/list-context";
import { db } from "~/lib/db"; // Prisma ORM database connection

// 本该每次导航到/items/[id]时执行,但现在仅页面刷新时运行
async function getChildItems(itemId: Item["id"]) {
  return await db.item.findMany({
    where: {
      parent_id: itemId,
    },
  });
}

export default async function ListLayout(params) {
  const items = await getChildItems(params.id);

  return (
    <ListProvider listId={params.id} items={items}>
      <div>{children}</div>
    </ListProvider>
  );
}

补充:db连接代码

import { PrismaClient } from "@prisma/client";
import { deletedAt } from "../prismaMiddleware";

declare global {
  // eslint-disable-next-line no-var
  var cachedPrisma: PrismaClient;
}

let prisma: PrismaClient;
if (process.env.NODE_ENV === "production") {
  prisma = new PrismaClient();
} else {
  if (!global.cachedPrisma) {
    global.cachedPrisma = new PrismaClient();
  }
  prisma = global.cachedPrisma;
}

prisma.$use(deletedAt);

export const db = prisma;

解决方法

1. 禁用服务器组件静态缓存

Next.js 13默认会缓存服务器组件的渲染结果,可通过两种方式强制每次请求重新取数:

  • 使用unstable_noStore:在组件中调用该函数禁用缓存
import { unstable_noStore as noStore } from 'next/cache';

export default async function ListLayout(params) {
  noStore(); // 禁用缓存,每次导航都重新执行数据查询
  const items = await getChildItems(params.id);

  return (
    <ListProvider listId={params.id} items={items}>
      <div>{children}</div>
    </ListProvider>
  );
}
  • 声明revalidate = 0:在组件顶部添加静态属性,等同于禁用缓存
export const revalidate = 0;

export default async function ListLayout(params) {
  const items = await getChildItems(params.id);

  return (
    <ListProvider listId={params.id} items={items}>
      <div>{children}</div>
    </ListProvider>
  );
}

2. 数据更新时主动失效缓存(推荐)

如果想保留缓存提升性能,可在数据更新的API路由中调用revalidatePath,精准失效对应页面的缓存:

// app/api/update-item/route.ts
import { db } from '~/lib/db';
import { revalidatePath } from 'next/cache';
import { NextResponse } from 'next/server';

export async function POST(request) {
  const { itemId, data } = await request.json();
  
  // 执行数据库更新
  await db.item.update({
    where: { id: itemId },
    data,
  });

  // 失效目标页面的缓存,下次访问时会重新生成服务端渲染内容
  revalidatePath(`/item/${itemId}`);

  return NextResponse.json({ success: true });
}

这种方式既保留了静态缓存的优势,又能确保数据变更后页面及时更新,是更优的方案。

3. 排除Prisma缓存干扰

你的Prisma客户端在开发环境采用全局缓存,但这仅复用客户端实例,不会缓存查询结果,所以核心问题还是Next.js的服务器组件缓存,无需修改Prisma连接代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 02:12:34