Comp AI CRM 前端性能实践:用模块级 Map 缓存重复函数调用(js-cache-function-results 规则详解)
后端前端CRM人工智能AI Agent【免费下载链接】crmComp AI CRM is an open source, CRM designed for AI agents. Agentic-first CRM.项目地址https://gitcode.com/gh_mirrors/crm48/crm点击查看免费下载本篇文章讲解 Comp AI CRM 仓库内置的 Vercel React 最佳实践技能中js-cache-function-results规则当同一函数在渲染期间被以相同输入反复调用时使用模块级Map缓存计算结果避免冗余计算。读完本文你将掌握两种可落地的缓存模式多值Map缓存与单值哨兵缓存、它们与useMemo的取舍以及该规则在 Comp AI CRM 前端源码中的实际印证可直接套用到列表渲染、格式化、查表等热路径场景。规则来源与定位该规则位于仓库内的技能包 .agents/skills/vercel-react-best-practices/rules/js-cache-function-results.md隶属于vercel-react-best-practices这一为 Agent 与 LLM 优化的 React 最佳实践结构化规则集。每条规则文件都带有 YAML frontmatter 元数据本规则如下字段值titleCache Repeated Function CallsimpactMEDIUMimpactDescriptionavoid redundant computationtagsjavascript, cache, memoization, performance在 .agents/skills/vercel-react-best-practices/rules/_sections.md 中规则按文件名前缀分区js-前缀属于Section 7JavaScript PerformanceJavaScript 性能微优化该分区整体 impact 为 LOW-MEDIUM定位是针对热路径的微优化累积起来能带来可观的改善。本规则在其内部被评为 MEDIUM中等收益核心目标只有一个避免冗余计算。问题场景渲染期内的重复计算规则原文给出了最典型的发生场景——组件渲染时对列表每一项调用开销较大的转换函数function ProjectList({ projects }: { projects: Project[] }) { return ( div {projects.map(project { // slugify() called 100 times for same project names const slug slugify(project.name) return ProjectCard key{project.id} slug{slug} / })} /div ) }这里的问题在于projects中的项目名是高度重复的例如同一客户的公司名、同一批导入的联系人公司名在多个列表中反复出现slugify()在每次渲染时对相同输入执行完全相同的计算React 组件在状态变化、父组件重渲染、路由切换时都会重新执行 render 函数体重复计算被成倍放大如果slugify()内部涉及正则、字符替换、多步清理这笔开销会随列表规模与渲染次数线性增长。规则的核心判断标准是同一函数、同一输入、渲染期间被多次调用。满足这三点就应该缓存。修复方案一模块级 Map 缓存规则给出的正确写法是在模块顶层声明一个Map用输入作为键、计算结果作为值// Module-level cache const slugifyCache new Mapstring, string() function cachedSlugify(text: string): string { if (slugifyCache.has(text)) { return slugifyCache.get(text)! } const result slugify(text) slugifyCache.set(text, result) return result } function ProjectList({ projects }: { projects: Project[] }) { return ( div {projects.map(project { // Computed only once per unique project name const slug cachedSlugify(project.name) return ProjectCard key{project.id} slug{slug} / })} /div ) }这个模式有几个值得拆解的技术点缓存放在模块级module scope而不是组件内部模块在进程生命周期内只初始化一次Map实例被所有组件实例、所有事件处理器共享缓存的生命周期远超单次渲染Map的键值对语义text输入→slug输出。Map.has()先做存在性检查命中则Map.get()直接返回未命中才计算并Map.set()写入——后续相同输入的调用全部变成 O(1) 查表!非空断言Map.get()返回string | undefined在has()确认存在后使用!断言类型这是 TypeScript 下的惯用写法调用方代码零侵入ProjectList内只需要把slugify(project.name)换成cachedSlugify(project.name)语义完全一致。修复方案二单值函数的哨兵缓存当缓存对象只有一个值时不必引入Map规则给出了更轻量的哨兵模式let isLoggedInCache: boolean | null null function isLoggedIn(): boolean { if (isLoggedInCache ! null) { return isLoggedInCache } isLoggedInCache document.cookie.includes(auth) return isLoggedInCache } // Clear cache when auth changes function onAuthChange() { isLoggedInCache null }要点在于用let声明模块级变量isLoggedInCache类型为boolean | null其中null就是尚未计算的哨兵值首次调用时计算并写入之后每次调用直接返回缓存值避免了document.cookie等 DOM/全局访问的重复开销必须提供失效入口onAuthChange()将缓存重置为null保证登录状态变化后重新计算。这是缓存模式不可省略的一半——只有缓存、没有失效策略就会读到过期数据。为什么是 Map 而不是 Hook规则原文特别强调了一句话Use a Map (not a hook) so it works everywhere: utilities, event handlers, not just React components.用 Map 而不是 Hook这样它在任何地方都能用工具函数、事件处理器而不仅仅是 React 组件。这与useMemo形成鲜明对比维度模块级 MapuseMemo作用域模块级全局共享组件实例级随组件挂载/卸载创建销毁适用范围工具函数、事件处理器、普通模块仅限 React 组件渲染内生命周期进程生命周期组件实例生命周期失效控制手动 clear完全自主依赖数组声明式控制心智负担需要自己管理React 管理useMemo的缓存挂在组件实例上组件卸载即失效且首次渲染必然计算模块级Map一旦填充跨渲染、跨组件、跨页面持久有效。因此对于输入空间有限且重复率高的纯转换函数模块级缓存是更彻底的方案。这也是为什么规则把它归入 Section 7JavaScript 性能而非 Re-render 优化分区——它解决的不只是 React 重渲染问题而是通用 JavaScript 层的重复计算问题。Comp AI CRM 源码中的实际印证这条规则并非纸上谈兵Comp AI CRM 前端代码库apps/app/lib中已有大量同类实践可以作为学习参照。用 Map 做 O(1) 分桶/去重apps/app/lib/contact-facts.ts 的factsByField用两个Map把联系人事实facts按字段分桶并利用has()保证每个字段只保留第一条记录export function factsByField(facts: ContactFact[]) { const applied new Mapstring, ContactFact(); const proposed new Mapstring, ContactFact(); for (const fact of facts) { const bucket fact.status APPLIED ? applied : proposed; if (!bucket.has(fact.field)) bucket.set(fact.field, fact); } return { applied, proposed }; }apps/app/lib/agent-results.ts 的anchorResults则在循环中用get/set完成按锚点分组收集const byAnchor new Mapstring, T[](); for (const { itemId, value } of anchored) { const bucket byAnchor.get(itemId); if (bucket) bucket.push(value); else byAnchor.set(itemId, [value]); }get命中即复用已有数组push未命中才set新建数组——这是先查表、命中即返回、未命中再计算的典型变体和cachedSlugify的has/get/set三连是同一套思维。类似的Map分组、计时器收集还出现在 apps/app/lib/agent-transcript.tsnew Mapstring, number()记录每轮耗时与 apps/app/lib/agent-builder-state.tsnew Mapstring, T()保存最新状态中。模块级一次性预计算同思想的查表替代计算apps/app/lib/deal-stage.ts 展示了另一种形态在模块加载时一次性构建好ORDER、PRESENTATION等只读常量之后所有组件渲染通过dealStageLabel(stage)、dealStagePresentation(stage)直接查表而不是每次渲染都重建映射const PRESENTATION: DealStagePresentation { DEMO_BOOKED: { label: Demo booked, tone: neutral }, QUALIFIED_TO_BUY: { label: Qualified to buy, tone: info }, // ... }; export function dealStageLabel(stage: DealStage): string { return PRESENTATION[stage].label; }这与cachedSlugify的底层逻辑一致——把重复计算前移到模块级执行一次运行时只做查表。区别仅在于deal-stage.ts的输入空间是有限的枚举静态预计算即可js-cache-function-results规则的输入空间未知因此用Map做按需惰性缓存。使用边界与注意事项从规则原文与源码实践可以提炼出几条明确的边界条件函数必须是纯函数同一输入永远产生同一输出且不依赖外部可变状态。cachedSlugify缓存的是text → slug的确定性映射而isLoggedIn依赖document.cookie因此规则特意给出onAuthChange失效钩子——只要输入依赖的外部状态可能变化就必须提供清缓存入口输入空间有限或重复率高才值得缓存如果输入几乎不重复如用户输入的搜索词、含时间戳的内容Map只会徒增内存且永远 miss需要清理时可配合 LRU 策略同目录下的server-cache-lru.md规则即讨论类似问题注意无界增长模块级Map不做自动淘汰长期运行的应用应结合业务为缓存设置上限或显式clear()单值哨兵注意null与真实值的区分若真实计算结果本身可能为null则isLoggedInCache ! null的哨兵判断会失效此时应考虑undefined哨兵或包装对象不需要过度工程化规则 impact 为 MEDIUM属于热路径微优化。如果转换函数本身极廉价、调用次数有限直接计算即可不必为了缓存而缓存。与同组规则的配合使用js-cache-function-results并非孤立存在它与 Section 7 的相邻规则构成完整的减少重复工作工具箱js-cache-property-accessrules/js-cache-property-access.md在循环中把obj.config.settings.value与arr.length提到循环外缓存减少属性查找次数——缓存的对象从函数结果延伸到属性访问js-set-map-lookupsrules/js-set-map-lookups.md把allowedIds.includes(item.id)的 O(n) 成员检查换成new Set(...).has(...)的 O(1) 检查——与Map.has()的查表思想完全同源。三者的共同原则是把每次都要算/查的东西变成算一次、查多次。函数结果用Map缓存、属性访问用局部变量缓存、成员判断用Set/Map数据结构适用场景互补可以组合使用。如何在本仓库查阅与维护这套规则vercel-react-best-practices是一个完整的规则体系阅读入口与维护方式如下目录 .agents/skills/vercel-react-best-practices/README.md 说明了规则集的结构rules/存放单条规则文件_sections.md为分区元数据、_template.md为新建规则的模板、AGENTS.md为构建产物、src/为构建脚本分区定义见 rules/_sections.md八个分区按性能影响排序Eliminating Waterfalls CRITICAL → Advanced Patterns LOWjs-规则属于第 7 分区若需在本地校验或重新生成规则汇总可进入该目录执行pnpm build编译规则到 AGENTS.md、pnpm validate校验规则文件、pnpm extract-tests提取用于 LLM 评测的测试用例新建规则时复制_template.md按area-description.md命名并使用对应前缀本规则的js-前缀即属于 JavaScript Performance 分区。小结js-cache-function-results给出的是一个克制而高效的模式当同一纯函数在渲染期间被相同输入反复调用时用模块级Map把结果缓存下来命中即返回、未命中才计算单值场景退化为let变量加null哨兵。选择Map而非 Hook是为了让缓存穿透组件边界作用于工具函数与事件处理器。Comp AI CRM 的 contact-facts.ts 分桶、agent-results.ts 分组与 deal-stage.ts 模块级预计算都是这一思想在生产代码中的落地形态——在动手优化之前先检查你的热路径上是否躺着大量可以只算一次的重复计算。赞分享后端前端CRM人工智能AI Agent【免费下载链接】crmComp AI CRM is an open source, CRM designed for AI agents. Agentic-first CRM.项目地址https://gitcode.com/gh_mirrors/crm48/crm点击查看免费下载相关推荐AutoGPT 前端性能守则用模块级 Map 缓存重复函数调用js-cache-function-results 规则详解AutoGPT 前端性能守则用模块级 Map 缓存重复函数调用js cache function results 规则详解 本文详解 AutoGPT 仓库人工智能AI Agent自主智能体Agent 工作流工作流自动化后端前端Phoenix 前端性能优化用模块级 Map 缓存重复函数调用js-cache-function-results 规则实战解析Phoenix 前端性能优化用模块级 Map 缓存重复函数调用js cache function results 规则实战解析 本篇技术指南围绕开源仓库可观测性AI 评测LLMOpsAI 应用人工智能Cherry Studio 的 React 性能规则详解用模块级 Map 缓存重复函数调用js-cache-function-resultsCherry Studio 的 React 性能规则详解用模块级 Map 缓存重复函数调用js cache function results 本篇解读 C人工智能大模型AI 应用交互助手本地部署上一篇终极指南GitHub Actions Ubuntu 22.04 运行器镜像全解析下一篇攻克Android 36适配难题GitHub Actions Runner Images平台扩展实战创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考