ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

cal.diy(cal.com)中的服务端性能实践:用 React.cache() 做请求级去重

cal.diy(cal.com)中的服务端性能实践:用 React.cache() 做请求级去重 cal.diycal.com中的服务端性能实践用 React.cache() 做请求级去重【免费下载链接】cal.diyScheduling infrastructure for absolutely everyone.项目地址: https://gitcode.com/GitHub_Trending/ca/cal.diy在 Next.js / React Server Components 应用中同一个页面请求内往往会有多个组件分别发起相同的认证查询或数据库读取造成重复的 DB 访问。本文基于 cal.diy 仓库内置的 server-cache-react 规则文档Vercel 出品、MIT 许可的 React 性能规则库中的一条 MEDIUM 级规则讲解React.cache()的请求级去重机制、适用边界并结合 cal.com 源码中真实的unstable_cache封装实现说明“请求内去重”与“跨请求缓存”在工程上如何各取所需。一、规则出处Vercel React Best Practices 规则库中的位置cal.diy 仓库在 agents/skills/vercel-react-best-practices 中内置了一份由 Vercel Engineering 维护的 React / Next.js 性能优化规则集共 45 条规则、8 个分类按影响面排序见 SKILL.md 中的优先级表优先级分类影响前缀1消除瀑布流CRITICALasync-2打包体积优化CRITICALbundle-3服务端性能HIGHserver-4客户端数据获取MEDIUM-HIGHclient-5~8重渲染 / 渲染 / JS / 高级模式MEDIUM ~ LOWrerender-等server-cache-react属于第 3 类“Server-Side Performance服务端性能”规则文件自身元信息frontmatter标注为标题Per-Request Deduplication with React.cache()影响等级MEDIUMimpactDescription: deduplicates within request标签server, cache, react-cache, deduplication完整的规则汇编版见 AGENTS.md 的 3.4 节约 L715-L735与本规则文件内容一致它的姊妹规则 server-cache-lru.md 则解决“跨请求缓存”问题。两条规则合起来覆盖了服务端数据获取的两种去重/缓存场景。二、规则要解决的问题同一请求内的重复查询规则文档给出的核心结论是一句话用React.cache()做服务端请求级去重收益最大的是认证authentication和数据库查询database queries这类场景。典型痛点是一个 RSC 页面中页面组件、侧边栏、导航栏各自独立地调用“获取当前用户”逻辑若该逻辑内部要await auth()拿会话再查一次数据库那么同一个 HTTP 请求内就会打出多次完全相同的查询。React.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 } }) })如规则原文所述在单个请求内多次调用getCurrentUser()只会真正执行一次查询。第二个及之后的调用直接复用第一次的结果对异步函数而言React 缓存的是返回的 Promise同一请求内并发调用共享同一个 Promise从而把 N 次会话解析 N 次findUnique收敛为 1 次。几个值得注意的机制细节它是“记忆化”而不是“缓存”React.cache()不引入任何过期时间、容量上限或持久化记忆化作用域就是一次渲染/一次请求请求结束即失效。因此它不承担“加速跨请求访问”的职责——这正是需要 LRU 的原因见第四节。适合无参或纯参函数规则示例中的getCurrentUser不接收参数。记忆化以参数身份为键参数来自不可变来源如当前请求内的稳定值时效果最确定带复杂可变参数的函数需谨慎评估。对认证链路的意义auth()这类函数通常内部包含 token 校验、会话存储读取甚至数据库查询是页面中扇出最广的公共依赖天然适合包一层cache()。三、边界请求内去重 vs 跨请求缓存同属server-分类的 server-cache-lru.md 明确划出了React.cache()的边界React.cache()only works within one request. For data shared across sequential requests用户先点了按钮 A、又点了按钮 B两个连续端点需要同一份数据use an LRU cache.该规则给出的 LRU 实现范式影响等级 HIGHimpactDescription: caches across requestsimport { LRUCache } from lru-cache const cache new LRUCachestring, any({ max: 1000, ttl: 5 * 60 * 1000 // 5 minutes }) export async function getUser(id: string) { const cached cache.get(id) if (cached) return cached const user await db.user.findUnique({ where: { id } }) cache.set(id, user) return user } // Request 1: DB query, result cached // Request 2: cache hit, no DB query适用条件是“用户在数秒内的连续操作会命中多个端点、且这些端点需要同一份数据”。规则同时指出了部署形态对 LRU 有效性的影响在函数实例可被并发请求共享的托管运行时中进程内 LRU 天然跨请求生效而在传统 serverless 中每次调用相互隔离跨进程缓存则需要 Redis 一类的共享存储。由此可以整理出清晰的选型表维度React.cache()LRU进程内/共享存储作用域单次请求跨请求受 TTL / 容量约束过期机制无请求结束即失效TTL 容量上限典型场景页面内多组件重复取数、认证解析连续操作命中多端点的同一数据部署约束无实例隔离时需外置存储四、cal.com 源码中的对照实践unstable_cache 封装从源码结构看cal.diy 的 Web 应用Next.js并没有直接依赖React.cache()而是把“请求去重/缓存”建立在 Next.js 的unstable_cache之上并做了仓库级的统一封装。这条实践链可以作为规则落地方式的真实参照。4.1 仓库级封装packages/lib/unstable_cachepackages/lib/unstable_cache/unstable_cache.ts 对next/cache的unstable_cache做了一层包装核心是解决序列化问题unstable_cache要求函数返回值可 JSON 序列化/** * This implementation is adapted from https://github.com/vercel/next.js/issues/51613#issuecomment-1892644565. * It is a wrapper around unstable_cache that adds serialization and deserialization */ import { unstable_cache } from next/cache; import { parse, stringify } from superjson; export const cache T, P extends unknown[]( fn: (...params: P) PromiseT, keys: Parameterstypeof unstable_cache[1], opts: Parameterstypeof unstable_cache[2] ) { const wrap async (params: unknown[]): Promisestring { const result await fn(...(params as P)); return stringify(result); }; const cachedFn unstable_cache(wrap, keys, opts); return async (...params: P): PromiseT { const result await cachedFn(params); return parse(result); }; };可以看到封装的要点入参以数组形式收集unstable_cache的键机制要求参数可序列化且多个参数需打包成单一数组参数传入出参用superjson的stringify/parse往返从而支持Date等原生类型。仓库内还有 getInstallCountPerApp.ts 等使用该封装的业务缓存函数。4.2 业务示例团队计划校验的缓存与失效apps/web/app/cache/membership.ts 展示了完整的“缓存 标签失效”闭环use server; import { MembershipRepository } from calcom/features/membership/repositories/MembershipRepository; import { NEXTJS_CACHE_TTL } from calcom/lib/constants; import { revalidateTag, unstable_cache } from next/cache; const CACHE_TAGS { HAS_TEAM_PLAN: MembershipRepository.hasAnyAcceptedMembershipByUserId, } as const; export const getCachedHasTeamPlan unstable_cache( async (userId: number) { const hasTeamPlan await MembershipRepository.hasAnyAcceptedMembershipByUserId(userId); return { hasTeamPlan: !!hasTeamPlan }; }, [getCachedHasTeamPlan], { revalidate: NEXTJS_CACHE_TTL, tags: [CACHE_TAGS.HAS_TEAM_PLAN], } ); export const revalidateHasTeamPlan async () { revalidateTag(CACHE_TAGS.HAS_TEAM_PLAN, max); };配套的参数与取值均有仓库依据revalidate取自常量NEXTJS_CACHE_TTL在 packages/lib/constants.ts 中定义为3600秒即 1 小时。tags缓存标签使用“仓库名 方法名”命名如MembershipRepository.hasAnyAcceptedMembershipByUserId便于定位缓存归属。失效方式revalidateTag(tag, max)将缓存 TTL 直接推进到最大即立刻失效——“读多写少 变更时定向失效”的组合而不是无差别清缓存。4.3 与规则的关系从源码结构看cal.com 的选择与规则文档并不矛盾而是分层互补React.cache()规则server-cache-react解决的是同一请求内的重复调用是 React 运行时的记忆化unstable_cache 封装 revalidateTagcal.com 现状解决的是跨请求的重复数据获取缓存键含函数键与参数userId带 TTL 与定向失效。也就是说若一个页面在同一请求内多处需要hasAnyAcceptedMembershipByUserId的结果unstable_cache在请求内同样只查一次库第二次命中缓存而跨请求的重复读取则完全由revalidateTTL 覆盖。这正是“请求级去重”与“跨请求缓存”两种手段在真实大型 Next.js 应用中如何协同的例证。五、落地建议小结结合规则文档与仓库实践可以给出可操作清单优先包装高频公共依赖会话/认证解析、当前用户查询、组织/团队读取等扇出最广的函数是React.cache()的第一梯队候选规则原文认证与数据库查询受益最大。认清作用域它只对单次请求内的多次调用去重请求之间零共享不要把业务正确性建立在“它缓存了结果”上。异步函数放心用cache()包裹async函数时同一请求内的并发调用共享同一个 Promise不会各自开查询。跨请求需求另选方案连续操作命中多端点、且数据在数秒内可复用用带 TTL 与容量上限的 LRU见 server-cache-lru.mdNext.js 应用也可直接使用unstable_cacherevalidateTag的仓库式封装参照 unstable_cache 封装 与 membership 缓存。缓存键与失效策略要显式无论哪种缓存都建议像 cal.com 那样为每个缓存定义命名标签并在数据变更点如成员关系变化调用定向失效避免依赖 TTL 自然过期造成的数据陈旧窗口。参考路径规则原文.opencode/skill/vercel-react-best-practices/rules/server-cache-react.md规则库总览.opencode/skill/vercel-react-best-practices/SKILL.md、汇编版 .opencode/skill/vercel-react-best-practices/AGENTS.md姊妹规则.opencode/skill/vercel-react-best-practices/rules/server-cache-lru.md仓库实践packages/lib/unstable_cache/unstable_cache.ts、apps/web/app/cache/membership.ts、packages/lib/constants.ts【免费下载链接】cal.diyScheduling infrastructure for absolutely everyone.项目地址: https://gitcode.com/GitHub_Trending/ca/cal.diy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表