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
相关产品推荐
相关产品推荐

