文章目录
- 前言
- Guidelines
- [JavaScript Patterns](#JavaScript Patterns)
-
- [Batch DOM CSS changes via classes](#Batch DOM CSS changes via classes)
- [Build index maps for repeated lookups](#Build index maps for repeated lookups)
- [Cache repeated function calls](#Cache repeated function calls)
- [Use toSorted() instead of sort() for immutability](#Use toSorted() instead of sort() for immutability)
- [Early length check for array comparisons](#Early length check for array comparisons)
- [JS 性能心法](#JS 性能心法)
前言
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
「样式一次性切换,不要逐条改」
核心问题
每一次:
js
el.style.xxx = ...
都可能触发:
- style recalculation
- layout
- paint
多次修改 = 多次成本
反例:逐条改 style
js
el.style.width = '200px'
el.style.height = '100px'
el.style.background = 'red'
推荐:class 批量切换
js
el.classList.add('active')
css
.active {
width: 200px;
height: 100px;
background: red;
}
浏览器只需要一次 style 计算
典型场景
- hover / active 状态
- 拖拽高亮
- 表格行选中
- 动态主题切换
一句话总结
DOM 改一次,CSS 管所有
Build index maps for repeated lookups
「多次查找,用 Map,不要 filter / find」
核心问题
js
array.find(...)
- O(n)
- 在 render / 循环中反复执行 → O(n²)
反例
ts
items.map(item => {
const user = users.find(u => u.id === item.userId)
return { ...item, user }
})
推荐:构建索引 Map
ts
const userMap = new Map(users.map(u => [u.id, u]))
items.map(item => {
const user = userMap.get(item.userId)
return { ...item, user }
})
复杂度从 O(n²) → O(n)
典型场景
- 表格数据 join
- id → entity 映射
- 权限校验
- diff 对比
一句话总结
先建索引,再查数据
Cache repeated function calls
「相同输入,不要重复算」
核心问题
ts
formatPrice(price)
如果:
- 在 render 中
- 在循环里
- 输入值重复
白算很多次
反例
ts
{items.map(i => (
<span>{formatPrice(i.price)}</span>
))}
推荐:简单 memo
ts
const cache = new Map()
function formatPriceCached(price) {
if (cache.has(price)) return cache.get(price)
const result = formatPrice(price)
cache.set(price, result)
return result
}
React 中更推荐:
ts
const formatted = useMemo(
() => formatPrice(price),
[price]
)
一句话总结
算过的值,记下来
Use toSorted() instead of sort() for immutability
「不要原地排序」
核心问题
js
array.sort()
- 原地修改
- 破坏不可变数据
- React 中可能导致 状态错乱
反例
ts
const sorted = items.sort((a, b) => a.age - b.age)
setItems(sorted)
items 被直接改了
推荐:toSorted(ES2023)
ts
const sorted = items.toSorted((a, b) => a.age - b.age)
setItems(sorted)
返回新数组,原数组不变
兼容方案
ts
[...items].sort(...)
一句话总结
React 状态 = 不可变
Early length check for array comparisons
「先比长度,再比内容」
核心问题
ts
arraysEqual(a, b)
如果:
- 长度不同
- 却仍然逐项对比
纯浪费
反例
ts
function isEqual(a, b) {
return a.every((v, i) => v === b[i])
}
推荐:提前剪枝
ts
function isEqual(a, b) {
if (a.length !== b.length) return false
return a.every((v, i) => v === b[i])
}
场景
- memo / shouldComponentUpdate
- diff 算法
- selection 对比
- 权限数组变化检测
一句话总结
先 cheap check,再 expensive check
JS 性能心法
少动 DOM
少扫数组
少算重复
不改原数据
先剪枝
总结
JS 性能不是靠"更快算法",而是靠"少做事"