ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Cherry Studio 的 React 性能规则 js-min-max-loop:用 O(n) 单次循环替代排序求最值

Cherry Studio 的 React 性能规则 js-min-max-loop:用 O(n) 单次循环替代排序求最值 Cherry Studio 的 React 性能规则 js-min-max-loop用 O(n) 单次循环替代排序求最值【免费下载链接】cherry-studio Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端项目地址: https://gitcode.com/CherryHQ/cherry-studio本篇围绕 Cherry Studio 仓库内置的 Agent 性能规则文件 js-min-max-loop.md 展开讲清“只取最小值/最大值时为什么不该先排序”这一 JavaScript 数组性能准则从复杂度推导、可复制的正误代码对照到Math.min/Math.max展开运算符的浏览器上限帮助你在写代码、做代码评审和重构 React/Electron 渲染层逻辑时直接替换掉“为了取一个值而整表排序”的浪费写法。这条规则在 Cherry Studio 规则体系中的位置Cherry Studio 在 .agents/skills/vercel-react-best-practices 下内置了一套来自 Vercel Engineering 的 React/Next.js 性能优化规则集共 62 条规则、8 个分类按影响程度impact排定优先级。SKILL.md 中的优先级总表如下优先级分类影响级别前缀1消除瀑布式等待 Eliminating WaterfallsCRITICALasync-2打包体积优化 Bundle SizeCRITICALbundle-3服务端性能 Server-SideHIGHserver-4客户端数据获取 Client-SideMEDIUM-HIGHclient-5重渲染优化 Re-renderMEDIUMrerender-6渲染性能 RenderingMEDIUMrendering-7JavaScript 性能 JavaScript PerformanceLOW-MEDIUMjs-8高级模式 Advanced PatternsLOWadvanced-js-min-max-loop属于第 7 类 JavaScript 性能规则前缀js-该分类共 13 条与它并列的还有js-early-exit尽早返回、js-combine-iterations合并多次遍历、js-set-map-lookups用 Set/Map 做 O(1) 查找、js-tosorted-immutable用toSorted()保持不可变等。规则文件的元数据由 frontmatter 声明js-min-max-loop.md的头部信息为--- title: Use Loop for Min/Max Instead of Sort impact: LOW impactDescription: O(n) instead of O(n log n) tags: javascript, arrays, performance, sorting, algorithms ---其中impact: LOW表示该规则的收益属于增量优化按 README.md 定义的 Impact LevelsCRITICAL 到 LOW 六级impactDescription直接点明收益本质——把 O(n log n) 降到 O(n)。这类规则面向 Agent 和 LLM 自动评审/重构场景所有规则文件可编译进 AGENTS.md 作为完整参考文档。问题本质排序取首元素是过度设计规则的核心论断只有一句话寻找最小或最大元素只需要对数组做一次线性遍历排序既浪费又慢。典型的错误写法是把整个数组复制一份、排序然后取第一个元素interface Project { id: string name: string updatedAt: number } function getLatestProject(projects: Project[]) { const sorted [...projects].sort((a, b) b.updatedAt - a.updatedAt) return sorted[0] }这里发生了三件多余的事[...projects]复制了整个数组——产生一次 O(n) 的内存分配.sort()对整个数组排序——V8 等引擎使用 TimSort 一类稳定排序平均复杂度 O(n log n)且需要 O(n) 额外空间最后只用到了sorted[0]——其余 n−1 个元素的相对顺序全部是白排的。“同时取最旧和最新”的变体同样有这个缺陷function getOldestAndNewest(projects: Project[]) { const sorted [...projects].sort((a, b) a.updatedAt - b.updatedAt) return { oldest: sorted[0], newest: sorted[sorted.length - 1] } }表面上“一次排序拿到两个值”似乎更划算但从算法角度看当目标只是 min/max 时排序仍然是不必要的 O(n log n)。数组规模越大差距越明显n 10 000 时 O(n log n) 约需 13 万量级的比较而 O(n) 只需 1 万次比较且没有复制与排序的临时内存开销。正确写法单次循环求最值O(n)规则给出的正确实现是“单趟遍历 边界判空”无复制、无排序function getLatestProject(projects: Project[]) { if (projects.length 0) return null let latest projects[0] for (let i 1; i projects.length; i) { if (projects[i].updatedAt latest.updatedAt) { latest projects[i] } } return latest } function getOldestAndNewest(projects: Project[]) { if (projects.length 0) return { oldest: null, newest: null } let oldest projects[0] let newest projects[0] for (let i 1; i projects.length; i) { if (projects[i].updatedAt oldest.updatedAt) oldest projects[i] if (projects[i].updatedAt newest.updatedAt) newest projects[i] } return { oldest, newest } }实现要点逐条拆解空数组短路函数入口先判断projects.length 0返回null或双空结构避免对空数组取projects[0]得到undefined后继续比较抛出异常以projects[0]作为初始候选不需要额外的-Infinity/Infinity哨兵或类型断言候选值始终是数组中的真实元素返回值类型保持Project而非数值从索引 1 开始比较元素 0 已经是候选跳过可省一次比较最值同趟获取getOldestAndNewest在一次循环里同时维护oldest与newest两个候选两个比较互不依赖总比较次数至多 2(n−1)仍然是 O(n)不修改原数组循环只读不写天然满足 React 数据流的不可变约束与同分类的 js-tosorted-immutable.md 关注的不可变性是一脉相承的。从源码结构看这套模式之所以被单独立为一条规则而不是散落在泛泛的“优化建议”里正是因为它在业务代码中极其常见按时间戳取最新会话、按高度/宽度取边界值、按分数取极值等场景都容易顺手写出“先 sort 再取 [0]”的直觉代码。替代方案Math.min / Math.max 的适用边界对于纯数值小数组规则也认可了更简的展开运算符写法const numbers [5, 2, 8, 1, 9] const min Math.min(...numbers) const max Math.max(...numbers)但文档明确警告其上限...spread会把数组元素展开成函数参数受限于引擎调用栈的参数上限——数组过长时不仅更慢还可能直接抛出错误。规则中给出的实测参考值是Chrome 143 中数组长度上限约 124000Safari 18 约 638000具体数值随浏览器版本变化。因此结论是小数组Math.min(...arr)/Math.max(...arr)可读性好可以使用大数组或长度不可控的数组例如列表数据、导入的表格行、搜索结果集一律用循环写法避免触碰展开上限对象数组如Project[]Math.min/max只接受数值无法直接比较updatedAt字段循环或先map出数值再循环是唯一可靠选择。仓库中的实际印证Math.min/Math.max 的普遍用法规则虽然以Project时间戳为例但它面向的正是像 Cherry Studio 这类 Electron React 桌面应用的渲染层代码。在 Cherry Studio 的 renderer 侧Math.min/Math.max已被大量用于“边界钳制”类场景例如src/renderer/components/QuickPanel/heights.ts计算快捷面板的布局高度时使用上下界钳制src/renderer/components/FilePreview/plugins/spreadsheet/XlsxGrid.tsx电子表格预览的行列尺寸计算src/renderer/components/Chart/charts/buildCartesianOption.ts、src/renderer/components/ModelSelector/ModelSelector.tsx 等组件中的尺寸/数值边界处理。这些位置处理的多是数值数组坐标、像素尺寸、行列数且规模有限使用Math.min/Math.max完全在规则认可的安全区间内。而当数据来自用户导入、文件解析或远程返回、长度不可控时就应改为本规则的单循环写法——这正是“小数组用 Math、大数组用循环”边界的真实应用场景。适用前提与限制影响级别 LOW这条规则属于增量优化不是 CRITICAL 级的“瀑布消除”或“打包瘦身”当数组本身只有几到几十项时排序与循环的性能差异在体感上不可察觉此时可优先关注可读性。规则的价值体现在长度不可控或数据量大的路径上仅适用于“只需要最值”的场景如果你确实需要返回有序列表或需要 Top-KK 较大元素排序/堆等其他手段才是正确工具本规则不适用比较器一致性循环写法直接比较的是你选定的键示例中为updatedAt不会出现sort默认按字典序排序导致字符串数字比较出错的经典坑规则维护流程该规则集通过 README.md 描述的pnpm build编译流程生成AGENTS.md与测试用例规则文件按文件名前缀自动归入分类、按标题排序新增规则只需遵循_template.md的 frontmatter 正误对照结构。小结js-min-max-loop这条规则给出的模式可以浓缩为一句话只需要最小/最大值时用一次带空值短路的线性扫描O(n)不要用“复制 排序 取首/尾”O(n log n)小数值数组可用Math.min/Math.max但大数组受展开运算符栈深限制应坚持循环写法。在 Cherry Studio 内置的 62 条性能规则体系中它属于第 7 类 JavaScript 性能准则是代码评审与 Agent 自动重构时可直接套用的判定项看到“sort 之后只取[0]或length - 1”即可替换为上文给出的单循环实现。【免费下载链接】cherry-studio Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端项目地址: https://gitcode.com/CherryHQ/cherry-studio创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表