文章目录
- 前言
- Guidelines
- JavaScript Patterns
- Batch DOM CSS changes via classes
- 核心问题
- 反例:逐条改 style
- 推荐:class 批量切换
- 典型场景
- 一句话总结
- Build index maps for repeated lookups
- 核心问题
- 反例
- 推荐:构建索引 Map
- 典型场景
- 一句话总结
- Cache repeated function calls
- 核心问题
- 反例
- 推荐:简单 memo
- React 中更推荐:
- 一句话总结
- Use toSorted() instead of sort() for immutability
- 核心问题
- 反例
- 推荐:toSorted(ES2023)
- 兼容方案
- 一句话总结
- Early length check for array comparisons
- 核心问题
- 反例
- 推荐:提前剪枝
- 场景
- 一句话总结
- JS 性能心法
- 总结
前言
react-best-practices
React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.
Guidelines
在这个系列,我会逐条拆解,每一条都给出:
- 核心问题是什么
- 为什么会慢(本质原因)
- 典型业务场景
- 反例代码
- 推荐写法
- 在 React / Next.js 中的实际收益
JavaScript Patterns
这是系列的第五部分。
这一部分其实非常“工程味”,不再是 React 专属,而是JS / DOM / 数据结构层面的硬功夫。
这些点看起来“小”,但在表格、列表、频繁交互里都是实打实的性能差异。
Batch DOM CSS changes via classes
「样式一次性切换,不要逐条改」
核心问题
每一次:
el.style.xxx=...都可能触发:
- style recalculation
- layout
- paint
多次修改 = 多次成本
反例:逐条改 style
el.style.width='200px'el.style.height='100px'el.style.background='red'推荐:class 批量切换
el.classList.add('active').active{width:200px;height:100px;background:red;}浏览器只需要一次 style 计算
典型场景
- hover / active 状态
- 拖拽高亮
- 表格行选中
- 动态主题切换
一句话总结
DOM 改一次,CSS 管所有
Build index maps for repeated lookups
「多次查找,用 Map,不要 filter / find」
核心问题
array.find(...)- O(n)
- 在 render / 循环中反复执行 → O(n²)
反例
items.map(item=>{constuser=users.find(u=>u.id===item.userId)return{...item,user}})推荐:构建索引 Map
constuserMap=newMap(users.map(u=>[u.id,u]))items.map(item=>{constuser=userMap.get(item.userId)return{...item,user}})复杂度从 O(n²) → O(n)
典型场景
- 表格数据 join
- id → entity 映射
- 权限校验
- diff 对比
一句话总结
先建索引,再查数据
Cache repeated function calls
「相同输入,不要重复算」
核心问题
formatPrice(price)如果:
- 在 render 中
- 在循环里
- 输入值重复
白算很多次
反例
{items.map(i=>(<span>{formatPrice(i.price)}</span>))}推荐:简单 memo
constcache=newMap()functionformatPriceCached(price){if(cache.has(price))returncache.get(price)constresult=formatPrice(price)cache.set(price,result)returnresult}React 中更推荐:
constformatted=useMemo(()=>formatPrice(price),[price])一句话总结
算过的值,记下来
Use toSorted() instead of sort() for immutability
「不要原地排序」
核心问题
array.sort()- 原地修改
- 破坏不可变数据
- React 中可能导致状态错乱
反例
constsorted=items.sort((a,b)=>a.age-b.age)setItems(sorted)items 被直接改了
推荐:toSorted(ES2023)
constsorted=items.toSorted((a,b)=>a.age-b.age)setItems(sorted)返回新数组,原数组不变
兼容方案
[...items].sort(...)一句话总结
React 状态 = 不可变
Early length check for array comparisons
「先比长度,再比内容」
核心问题
arraysEqual(a,b)如果:
- 长度不同
- 却仍然逐项对比
纯浪费
反例
functionisEqual(a,b){returna.every((v,i)=>v===b[i])}推荐:提前剪枝
functionisEqual(a,b){if(a.length!==b.length)returnfalsereturna.every((v,i)=>v===b[i])}场景
- memo / shouldComponentUpdate
- diff 算法
- selection 对比
- 权限数组变化检测
一句话总结
先 cheap check,再 expensive check
JS 性能心法
少动 DOM
少扫数组
少算重复
不改原数据
先剪枝
总结
JS 性能不是靠“更快算法”,而是靠“少做事”