【免费下载链接】open-slideA slide framework built for agents.项目地址https://gitcode.com/gh_mirrors/op/open-slide点击查看免费下载在 React / Next.js 应用中最常见的性能隐患之一是在循环或渲染过程中反复调用Array.prototype.find()、includes()等线性查找方法导致整体复杂度退化为 O(n×m)。本文聚焦 open-slide 仓库内置的 Vercel React Best Practices 技能集中js-index-maps这条规则规则原文从复杂度分析、改造前后代码对比、适用边界到仓库源码中的真实实践完整讲解如何通过一次性构建索引 Map把重复按键查找从 O(n) 降到 O(1)并将 1000×1000 规模的 1M 次操作压缩到约 2K 次。规则出处Vercel 性能规则的第七大类js-index-maps是 open-slide 仓库内.agents/skills/vercel-react-best-practices/技能集下的一条规则文件。该技能集由 Vercel 工程团队维护共包含 70 条规则、按影响优先级划分为 8 大类专门面向 Agent 与 LLM 在编写、审查或重构 React / Next.js 代码时使用见 SKILL.md。按 _sections.md 的分节定义本条规则隶属于第 7 节「JavaScript Performancejs」其定位是热路径上的微观优化优先级类别影响等级文件名前缀7JavaScript PerformanceLOW-MEDIUMjs-规则 frontmatter 中的元数据也明确了这条规则的定位与收益预期title: Build Index Maps for Repeated Lookups impact: LOW-MEDIUM impactDescription: 1M ops to 2K ops tags: javascript, map, indexing, optimization, performance在技能集的汇总文档 AGENTS.md 第 7.2 节Build Index Maps for Repeated Lookups中同样收录了这条规则说明它属于「增量式收益」但「在多处重复查找场景下可累计成显著改善」的优化模式。问题场景循环内反复 find() 的平方级代价规则的触发条件非常明确——当同一批数据在循环体内被反复按相同键查找时就应该改用 Map。典型的错误写法是在遍历订单时为每个订单线性扫描用户列表function processOrders(orders: Order[], users: User[]) { return orders.map(order ({ ...order, user: users.find(u u.id order.userId) })) }这段代码的问题在于orders.map()每迭代一次内部的users.find()都要从头到尾或到命中位置扫描一次users数组。假设有 n 个订单、m 个用户总比较次数是 n × m外层map遍历 n 个订单每次迭代调用find最坏情况下比较 m 个用户。因此总复杂度为O(n×m)。当 n 与 m 都达到上千规模时例如渲染一张包含 1000 个订单、关联 1000 个用户的数据表比较次数将达到 100 万次量级。对find每次返回的新对象引用还会带来额外的 GC 压力——这正是规则中「Multiple.find()calls by the same key should use a Map」的直接原因。核心改造一次建索引查询变 O(1)规则给出的正确写法是先把用户数组「索引化」成一个以id为键的Map再在循环中改用Map.get()function processOrders(orders: Order[], users: User[]) { const userById new Map(users.map(u [u.id, u])) return orders.map(order ({ ...order, user: userById.get(order.userId) })) }改造后的成本结构发生了质变构建阶段users.map(u [u.id, u])遍历一次用户数组生成键值对new Map(...)再遍历一次插入整体耗时O(n)查询阶段Map.get(order.userId)基于哈希查找平均O(1)不再随用户数量增长。正如规则原文所总结的「Build map once (O(n)), then all lookups are O(1)」。以规则给出的量级估算——1000 个订单 × 1000 个用户改造前1000 × 1000 1,000,0001M次比较改造后构建 Map 约 1000 次插入 1000 次 O(1) 查询 ≈2,0002K次操作。这正是 frontmatter 中impactDescription: 1M ops to 2K ops的由来——在数据规模不变的前提下总操作量降低约三个数量级。适用边界与变体何时建索引、如何处理查不到的情况js-index-maps这条规则看似简单实战中仍有几个关键判断点需要掌握。什么时候值得建 Map重复查找同一数据源在循环、嵌套循环或多个组件渲染路径中多次按同一键查找且数据量较大几十以上时收益明显n×m 形态的关联join操作两个集合需要按键关联如订单↔用户、评论↔文章、文件↔所属文件夹本质是数据库 JOIN 的纯前端等价物一次性构建、多次使用Map 在循环外构建一次循环体内只做 O(1) 读取。如果只是单次查找、数据量极小个位数直接find的代码可读性更好不必强行引入 Map——此时优化收益可忽略不计。查不到键时find与Map.get的语义差异两种写法在「查无此人」时的返回值有所不同需要注意users.find(u u.id order.userId)找不到时返回undefineduserById.get(order.userId)找不到时同样返回undefined若 Map 中显式存过undefined值则返回该值。在大多数场景下两者行为一致可以直接替换。若需要区分「键不存在」与「值为 undefined」可先用userById.has(key)判断再取get(key)。单键之外的变体多键索引与 Set 查重当关联条件不是单一键、而是「键的组合」时可以嵌套 Map 或使用复合键// 两级索引先按 userId再按 role const byUserAndRole new Map( users.map(u [u.id, new Map(u.roles.map(r [r, u]))]) )当目的是判断元素是否存在而非取值时Set是更轻量的选择详见同技能集的 js-set-map-lookups 规则allowedIds.includes()→allowedIds.has()同样把 O(n) 的成员判断降为 O(1)。此外与 js-cache-function-results模块级 Map 缓存重复函数调用和 js-cache-property-access循环内缓存属性访问等规则组合使用可以系统性地消除热路径上的重复工作。为什么不建议用普通对象做索引规则推荐Map而非{ [id]: user }字面量对象主要基于两点其一Map 的键可以是任意类型包括对象、数字等普通对象键会被强制转换为字符串容易发生键冲突其二Map 自带get/has/size语义清晰迭代顺序为插入顺序且不存在原型链属性如constructor被误命中的隐患。因此当索引键不是纯字符串时应优先使用 Map。仓库源码佐证open-slide 中的索引 Map 实战open-slide 核心包packages/core中就有多处与这条规则完全一致的实践可作为「正确写法」的活例。示例一文件夹重排时的 byId 索引在 packages/core/src/app/lib/folders.ts#L182-L197 的reorder逻辑中代码先把文件夹清单构建成id → Folder的 Map再对新的 id 序列逐个 O(1) 查回原对象const reorder useCallback( async (ids: string[]) { const prev manifest; const byId new Map(prev.folders.map((f) [f.id, f])); const next ids.map((id) byId.get(id)).filter((f): f is Folder Boolean(f)); if (next.length ! prev.folders.length) return; setManifest({ ...prev, folders: next }); try { await putReorder(ids); } catch (err) { setManifest(prev); throw err; } }, [manifest], );这里的byId索引 Map 恰好完整复现了规则的两个要点循环外一次构建new Map(prev.folders.map((f) [f.id, f]))、循环内 O(1) 查询ids.map((id) byId.get(id))。如果换成prev.folders.find(f f.id id)每次重排将退化为 O(ids × folders) 的线性扫描。示例二文本编辑快照的 DOM 节点上下文索引在 packages/core/src/app/components/inspector/inspector-provider.tsx#L292-L310 的textEditHtml中代码把文本快照的每个元素映射到其空白处理值再在后续遍历中通过contexts.get(node)快速查询const contexts new Map( textSnapshotElements(preview).map((element, index) [element, snapshot.whiteSpaces[index]]), ); const whiteSpace: WhiteSpaceResolver (element) { for (let node: HTMLElement | null element; node; node node.parentElement) { const value contexts.get(node); if (value ! undefined) return value; } return normal; };这段代码同时展示了索引 Map 的两个进阶用法键为 DOM 节点对象Map 支持任意类型键普通对象无法胜任以及get返回undefined时的回退处理沿父链向上逐级查询直到命中或返回默认值。这两个实例证明js-index-maps不是停留在文档里的教条而是 open-slide 核心编辑器代码中正在使用的工程模式。作为 Agent 技能的落地方式本条规则随技能集以.agents/目录形式分发.agents/skills/vercel-react-best-practices/其目标读者首先是维护、生成或重构 React / Next.js 代码的 Agent 与 LLM。在代码审查或自动重构流程中可将「循环体内出现按同一键的.find()/.includes()」作为触发信号自动将其改写为「循环外构建 Map/Set 循环内get/has」的形态。结合本仓库的具体工作流幻灯片框架的实时渲染与可视化编辑这类优化尤其适合出现在数据表格/列表页的关联字段渲染、侧边栏树状结构的 id 查找、缩略图与幻灯片对象之间的映射等热路径上。建议与技能集中其余js-前缀规则如 js-set-map-lookups、js-cache-function-results、js-combine-iterations配合阅读形成一套完整的「消灭热路径重复工作」的检查清单。小结js-index-maps的核心主张可以浓缩为一句话凡是循环体内反复出现的同键线性查找都应升级为循环外一次性构建的索引 Map。它把 O(n×m) 的关联操作降为 O(n) 建表 O(m) 查询在 1000×1000 规模下即可实现 1M ops → 2K ops 的数量级收益。open-slide 仓库既以.agents/技能文件的形式分发这条规则也在packages/core的编辑器与文件夹管理代码中亲身实践了该模式为 Agent 驱动的性能优化工作流提供了文档与实现互相印证的完整范例。赞分享【免费下载链接】open-slideA slide framework built for agents.项目地址https://gitcode.com/gh_mirrors/op/open-slide点击查看免费下载相关推荐open-agents 性能规则实践用索引 Map 把重复查找从 O(n) 降到 O(1)open agents 性能规则实践用索引 Map 把重复查找从 O n 降到 O 1 open agents 仓库内置了一套面向 Agent 与 LLM 的人工智能AI Agent代码智能体Agent 工作流Agent 沙箱工具调用后端前端用索引 Map 消除 O(n²) 重复查找Polar 前端性能优化实战js-index-maps 规则深度解析用索引 Map 消除 O n² 重复查找Polar 前端性能优化实战js index maps 规则深度解析 导读 Polar 的前端工程 client后端前端金融科技Sanity 仓库实战用 Map 构建索引表把重复查找从 O(n²) 压到 O(1) —— 详解 vercel-react-best-practices 的 js-index-maps 规则Sanity 仓库实战用 Map 构建索引表把重复查找从 O n² 压到 O 1 —— 详解 vercel react best practices 的 jCMS前端上一篇HsMod配置实战从入门到精通的炉石插件指南下一篇pythae模型比较如何选择最适合你任务的VAE算法创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考