优先使用 classname 切换预定义 css 类而非直接操作 style,避免强制同步重排;动态加样式用 insertrule 而非插入 style 标签;批量 dom 更新用 documentfragment 或 display: none 隔离;读取布局属性需集中前置,避免读写混杂。

直接改 className 比反复操作 style 属性安全得多
浏览器遇到内联样式频繁变更(比如循环里写 element.style.color = 'red'),会强制触发同步重排。哪怕只是改颜色,如果元素有浮动、定位或依赖尺寸的布局逻辑,就可能连带触发重排+重绘。用 className 切换预定义好的 CSS 类,把样式变更收束到一次渲染帧里,浏览器能批量处理。
- 所有动态样式变更,优先走 CSS 类 +
classList.toggle()/classList.add() - 避免在
for循环、scroll事件、requestAnimationFrame回调里直接写element.style.xxx - 需要临时高亮某组元素?提前写好
.highlighted { background: yellow; },运行时只改类名
insertRule 动态加样式表比拼接 <style></style> 标签更可控
用 document.createElement('style') 然后 appendChild 插入大段 CSS 字符串,会立刻触发样式计算和重排。而通过 CSSStyleSheet.insertRule() 把规则加进已存在的样式表(比如 document.styleSheets[0]),规则只是“注册”了,不立即生效——直到后续 DOM 变更或下一帧才参与计算,节奏更可控。
- 先用
document.styleSheets[0].insertRule('.new-btn { padding: 4px; }', 0)注册规则 - 再统一给元素加
className="new-btn",样式才真正应用 - 注意:IE 不支持
insertRule,如需兼容得 fallback 到addRule(仅 IE) - 别在循环里反复调用
insertRule,每条规则都算一次样式表变更开销
批量 DOM 更新前用 documentFragment 或 display: none 隔离渲染
一次性插入几十个带样式的节点,如果逐个 appendChild,浏览器每插一个都可能重排。把它们先塞进 documentFragment,或者先把父容器设为 display: none,等全部挂载完再显示,就能把重排压缩成一次。
const frag = document.createDocumentFragment(); for(...) frag.appendChild(item); parent.appendChild(frag);- 或者:
parent.style.display = 'none'; /* 批量操作 */; parent.style.display = ''; - 注意:
display: none会让元素脱离文档流,如果操作中依赖offsetHeight等尺寸值,会拿不到正确结果 -
documentFragment是更干净的选择,不影响布局测量
慎用会强制同步布局的属性读取
只要在写样式之后、下一个重排之前,读取像 offsetTop、getComputedStyle()、clientWidth 这类属性,浏览器就不得不立刻计算当前样式并重排——这会打断渲染流水线,把本可合并的多次变更拆成多次重排。
立即学习“前端免费学习笔记(深入)”;
- 把所有读取操作集中在一起,放在所有写操作之前(“读-写-读-写”改成“读-读-写-写”)
- 避免在循环里边写边读:
el.style.left = x + 'px'; console.log(el.offsetLeft);→ 这句offsetLeft就是雷 -
getComputedStyle(el).color同样触发强制重排,除非你真需要此刻的计算值 - 现代方案:用
ResizeObserver或IntersectionObserver替代轮询式尺寸监听
CSS 的重排重绘成本藏在细节里:不是“加了样式就卡”,而是“读写混杂+高频触发+未隔离变更”共同拖慢渲染。最稳妥的做法,是把样式变更尽量收敛到单次 DOM 操作、单次类名切换、单次样式表注入,并彻底避开强制同步布局的读取时机。










