Next.js动态路由中tRPC结合getServerSideProps实现SSR的问题
问题描述
我刚启动了一个基于完整T3栈(Next.js、Prisma、Tailwind、tRPC)的小型项目,遇到了以下问题:
背景:我在PlanetScale(pscale)中有一个artists表,包含字段name、email、address、id、url,该表关联着art表(字段artName、artPrice、artDimensions)。我希望根据艺术家的url参数,在pages/artists/[url].tsx页面展示对应艺术家的内容。
我在trpc/router/artist.ts中配置了路由:
export const artistRouter = router({ // 根据url查询艺术家 byUrl: publicProcedure .input( z.object({ url: z.string(), }) ) .query(async ({ input }) => { const { url } = input; const artist = await prisma.artist.findUnique({ where: { url }, select: defaultArtistSelect, }); // 注:原代码中错误使用了dealer,此处修正为artist if (!artist) { throw new TRPCError({ code: 'NOT_FOUND', message: `No artist with url '${url}'`, }); } return artist; }), });
在[url].tsx页面中,用客户端查询的方式获取数据会出现短暂加载状态,首次查询时数据为undefined:
const url = useRouter().query.url as string; const { data: artist, isError, isLoading, isSuccess } = trpc.artist.byUrl.useQuery({ url });
我希望为该动态路由实现SSR,且统一使用tRPC(不想直接用Prisma查询,因为其他场景如管理视图已在使用tRPC),但始终无法让getServerSideProps与tRPC配合工作。
更新:后来发现了tRPC文档中的SSG Helpers,解决了getServerSideProps的问题,但现在请求速度非常慢。
解决方案
1. 正确使用tRPC的SSG Helpers实现SSR
首先确保你已按T3栈规范配置了SSG Helpers(示例路径:server/trpc/ssgHelpers.ts):
// server/trpc/ssgHelpers.ts import { appRouter } from "./router"; import { createTRPCContext } from "./trpc"; import { createServerSideHelpers } from "@trpc/react-query/server"; import superjson from "superjson"; export const createSSGHelpers = async () => { return createServerSideHelpers({ router: appRouter, ctx: await createTRPCContext({ req: null, res: null }), transformer: superjson, }); };
然后在[url].tsx的getServerSideProps中调用该Helper预取数据:
// pages/artists/[url].tsx import { createSSGHelpers } from "../../server/trpc/ssgHelpers"; import { trpc } from "../../utils/trpc"; import { useRouter } from "next/router"; export async function getServerSideProps(context) { const { url } = context.params; const ssg = await createSSGHelpers(); try { // 预取艺术家数据 await ssg.artist.byUrl.prefetch({ url }); } catch (error) { // 捕获NOT_FOUND错误,返回404 return { notFound: true, }; } return { props: { trpcState: ssg.dehydrate(), }, }; } export default function ArtistPage() { const router = useRouter(); // 直接获取预取的数据,无客户端加载状态 const { data: artist } = trpc.artist.byUrl.useQuery({ url: router.query.url as string }); return ( <div className="container mx-auto p-4"> <h1 className="text-2xl font-bold mb-4">{artist.name}</h1> <p className="mb-2">邮箱:{artist.email}</p> <p className="mb-4">地址:{artist.address}</p> <h2 className="text-xl font-semibold mb-2">作品列表</h2> <div className="grid grid-cols-1 md:grid-cols-3 gap-4"> {artist.art.map((item) => ( <div key={item.id} className="border p-3 rounded"> <h3>{item.artName}</h3> <p>价格:{item.artPrice}</p> </div> ))} </div> </div> ); }
2. 优化请求速度的方法
如果使用SSG Helpers后速度慢,可从以下几点优化:
- 添加数据库索引:确保
artists表的url字段添加唯一索引,提升findUnique查询效率。在Prisma Schema中修改:
运行model Artist { id String @id @default(cuid()) name String email String? address String? url String @unique // 添加唯一索引 art Art[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt }prisma db push或prisma migrate dev更新数据库。 - 精简查询字段:确认
defaultArtistSelect只包含页面所需字段,关联art表时按需获取数据,避免冗余查询。 - 缓存查询结果:若艺术家数据不频繁更新,可在
getServerSideProps中加入Redis等缓存逻辑,减少数据库查询次数。 - 检查上下文初始化:确保
createTRPCContext中没有重复创建Prisma客户端(T3栈默认已配置单例客户端,无需额外实例化)。
内容的提问来源于stack exchange,提问作者Louis Stephens
相关产品推荐
相关产品推荐

