FEATURED · 精选文章

open-agents 中的 React.cache() 单请求去重:服务端数据获取按需缓存实战

发布时间 / 2026/9/17 2:12:42
来源 / 创域科博编辑部
栏目 / 资讯中心
open-agents 中的 React.cache() 单请求去重:服务端数据获取按需缓存实战 open-agents 中的 React.cache() 单请求去重服务端数据获取按需缓存实战【免费下载链接】open-agentsAn open source template for building cloud agents.项目地址: https://gitcode.com/GitHub_Trending/op/open-agents本篇基于 open-agents 仓库内置的 Vercel React 最佳实践规则server-cache-reactPer-Request Deduplication with React.cache()展开。该规则指出在 React Server Components 场景下同一个请求的渲染树内可能存在对同一份服务端数据的多次获取鉴权、数据库查询、重计算等React.cache()可在单请求生命周期内对这些调用做自动去重。读完本文你将掌握React.cache()的缓存命中语义浅等值Object.is、参数设计的正确姿势避免内联对象以及 open-agents 项目中鉴权、会话查询等真实模块是如何落地这一模式的。规则定位服务端性能类别下的 MEDIUM 级优化该规则文件位于 server-cache-react.md其 frontmatter 声明了如下元信息title: Per-Request Deduplication with React.cache()impact: MEDIUM中等收益请求内去重tags: server, cache, react-cache, deduplication按照该技能库 README 中的命名规范server-前缀对应 Server-Side Performance服务端性能分区。也就是说React.cache()被归类为服务端性能优化手段核心收益是deduplicates within request——只在单个请求内生效不做跨请求缓存。这一点是理解后续所有细节的前提它不是数据库连接池也不是全局 memoization而是这一次 HTTP 请求渲染整棵组件树时同一个函数用同样的入参只算一次。核心用法用 cache() 包裹异步服务端函数规则给出的标准用法是用cache()包裹一个异步函数让同一请求内的多次调用只执行一次真实工作。以鉴权为例import { cache } from react export const getCurrentUser cache(async () { const session await auth() if (!session?.user?.id) return null return await db.user.findUnique({ where: { id: session.user.id } }) })在同一个请求中无论组件树里有多少个 Server Component 或 Server Action 调用了getCurrentUser()底层的auth()会话解析和数据库查询只会真正执行一次后续调用直接拿到第一次调用产生的结果包括异常——第一次调用抛出时后续调用也会直接重新抛出该错误而不是重试。open-agents 的鉴权去重getServerSessionopen-agents 对这一模式的落地见 get-server-session.tsimport { headers } from next/headers; import { cache } from react; import { auth } from /lib/auth/config; import type { Session } from ./types; export const getServerSession cache( async (): PromiseSession | undefined { const baSession await auth.api.getSession({ headers: await headers(), }); if (!baSession?.user) { return undefined; } return { created: baSession.session.createdAt.getTime(), authProvider: vercel, user: { id: baSession.user.id, username: extractUsername(baSession.user), email: baSession.user.email ?? undefined, avatar: baSession.user.image ?? , name: baSession.user.name ?? undefined, }, }; }, );这里有两个值得注意的实现细节无参函数 请求级副作用getServerSession不接受任何参数因此天然满足缓存命中条件它内部await headers()读取请求头来解析 Better Auth 会话这在一次请求中是稳定值适合只取一次。去重范围真实存在从源码引用统计看getServerSession()被apps/web下 30 余个 API 路由和页面文件调用如 sessions 路由、chat 上下文、repo 页面 等。对于单个请求同时触发页面渲染与多个数据读取的复杂路由而言cache()保证了会话解析不会随调用点数量线性放大。open-agents 的数据访问去重带参 cache 函数sessions-cache.ts 展示了带参数的 cache 包装——注意其参数设计直接呼应了下一节的关键规则import { cache } from react; import { getSessionById, getShareById } from ./sessions; export const getSessionByIdCached cache(async (sessionId: string) getSessionById(sessionId), ); export const getShareByIdCached cache(async (shareId: string) getShareById(shareId), );getSessionByIdCached把 sessions.ts 中的原始数据库查询Drizzledb.select().from(sessions)等包了一层请求级去重。同一请求里多个组件都需要当前会话记录时例如页面数据与权限校验各查一次只执行一次 SQL。更复杂的例子是 public-usage-profile.ts 中的getPublicUsageProfileexport const getPublicUsageProfile cache( async ( username: string, dateValue: string | null, ): PromisePublicUsageProfile | null { // 1) 按 username 查 users 表join userPreferences // 2) 解析日期参数构造查询范围 // 3) Promise.all 并行执行 getUsageHistory getUsageInsights // 4) 汇总出公开 usage 档案totals / topModels / dailyActivity ... }, );这个函数内部包含用户查询、日期解析以及并行的两条聚合查询属于典型的重活非常适合请求级去重。两个参数username: string与dateValue: string | null都是原始类型这正是命中缓存的关键——下面展开。缓存命中的判定规则浅等值Object.is与参数设计React.cache()使用浅等值Object.is来判定两次调用的参数是否一致。这一判定方式引出一个高频陷阱内联对象每次调用都会创建新引用永远无法命中缓存。错误示例永远 missconst getUser cache(async (params: { uid: number }) { return await db.user.findUnique({ where: { id: params.uid } }) }) // 每次调用都创建新对象永远不会命中缓存 getUser({ uid: 1 }) getUser({ uid: 1 }) // 缓存未命中查询再跑一次正确示例命中const getUser cache(async (uid: number) { return await db.user.findUnique({ where: { id: uid } }) }) // 原始类型参数按值比较 getUser(1) getUser(1) // 命中缓存直接返回缓存结果如果必须传对象请传入同一个引用const params { uid: 1 } getUser(params) // 执行查询 getUser(params) // 命中缓存同一个引用总结成三条可操作的参数设计规范参数形态命中行为建议原始类型string / number / boolean / null / undefined按值比较Object.is相同值即命中首选open-agents 的sessions-cache.ts即此风格同一引用复用先声明常量再多次传入按引用比较命中对象必须传时采用内联对象字面量每次都是新引用必然 miss避免cache()形同虚设open-agents 中带参 cache 函数的实践getSessionByIdCached(sessionId: string)、getPublicUsageProfile(username: string, dateValue: string | null)恰好全部落在第一行以 URL 路由参数这类天然原始类型的字符串作为入参既简单又稳定地保证同一请求内的去重效果。Next.js 语境下的边界fetch 已自动去重其余异步工作才需要 cache()规则文档特别标注了一个 Next.js 专属说明Next.js 已为fetchAPI 自动扩展了请求内 memoization——相同 URL 与相同 options 的fetch请求在单个请求内自动去重因此对fetch调用不需要再套React.cache()。但fetch之外的异步工作没有任何自动去重React.cache()对它们是必需品。规则明确列出的受益场景包括数据库查询Prisma、Drizzle 等 ORM重计算Heavy computations鉴权检查Authentication checks文件系统操作任何非 fetch 的异步任务open-agents 恰好是数据库查询 鉴权 重计算三类场景的全集Drizzle ORM 查询sessions.ts、public-usage-profile.ts、Better Auth 会话解析get-server-session.ts、usage 档案的聚合计算。从源码结构看项目把会被一次请求多次触达的服务端数据获取函数统一包上cache()而页面级fetch如 API 客户端调用则依赖 Next.js 自身的 fetch 去重——两者分工与规则文档的指引完全一致。使用时的注意事项与适用边界结合规则内容与 open-agents 的实际写法落地时有三点需要留意作用域是单请求而非单页或单进程。请求结束缓存即失效下一个请求会重新执行。不要把它当作减轻数据库长期压力的手段——它解决的是同一次渲染中重复劳动不是全局少查库。无参函数命中率最高也最容易写错成有参函数。像getServerSession这样读取headers()的函数无需参数若你给它加了userId等参数就要重新审视这些参数的等值语义。cache()包裹的是 Promise 结果的复用不是并发锁之外的执行保护。同一请求内第一次调用尚未 resolve 时后续相同调用的行为依赖于 React 的缓存语义实现从源码结构看open-agents 未对其做额外的并发防抖处理依赖的是 React 的默认行为。如果你的场景对并发重复执行敏感例如昂贵的文件 IO可以在此基础上自行补充说明或测试验证。与 React Server Components 的关系cache()由react包直接导出在 RSC 的渲染边界内生效在纯客户端组件中调用它没有请求语义可言。小结一条规则三处落地server-cache-react规则的核心信息可以浓缩为三句话React.cache()提供请求级去重命中判定基于Object.is浅等值所以参数要用原始类型或复用引用Next.js 中fetch已经自带去重数据库、鉴权、重计算等其他异步工作才需要显式包装。open-agents 中 getServerSession、getSessionByIdCached / getShareByIdCached 与 getPublicUsageProfile 三处实现分别对应了规则中鉴权检查与数据库查询两大受益场景并且全部遵循了原始类型参数的设计规范可以作为该规则在生产型 Next.js 项目中的参考样本。规则原文参见 server-cache-react.md同目录下的 README 说明了该技能库按server-前缀对服务端性能规则的分组方式可对照阅读其他server-*规则如并行获取、序列化优化形成完整的 RSC 性能优化清单。【免费下载链接】open-agentsAn open source template for building cloud agents.项目地址: https://gitcode.com/GitHub_Trending/op/open-agents创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻