重排、重绘、合成:浏览器渲染的“三兄弟”,你惹不起也躲不过
你给一个元素悄悄改了宽度结果整个页面都抖了一下你加了个动画电脑风扇开始狂转今天我们来认识浏览器渲染里的“三兄弟”——重排、重绘、合成。弄懂它们你就能写出流畅60帧的页面告别卡顿。前言想象一下你家客厅要重新装修。你只是换了个抱枕重绘很轻松。但如果你要把墙拆了重排那得搬家具、砸墙、重新粉刷累得半死。如果只是把电视画面换个图层合成连工人都不要遥控器一按就行。浏览器的渲染也是这个道理。理解这三种操作的成本就能写出性能飞起的页面。一、先复习渲染流水线之前我们讲过浏览器把HTML/CSS变成屏幕上的像素要经过DOM树 CSSOM树 → 渲染树 → 布局计算位置大小→ 绘制填充像素→ 合成合并图层。其中重排Reflow重新计算布局位置、大小。成本最高。重绘Repaint重新绘制像素颜色、背景、阴影等。成本中等。合成Composite重新合并图层。成本极低走GPU。二、重排动到筋骨全员遭殃什么操作会触发重排改变元素的几何属性width、height、margin、padding、border、top、left……改变DOM结构增删元素、改变内容文字变了导致高度变化。读取某些布局属性offsetTop、scrollTop、clientWidth、getComputedStyle()。因为浏览器需要返回最新值不得不强制重排。改变窗口大小resize事件。激活伪类如:hover导致样式变化影响布局。重排的代价浏览器要重新计算整个或部分渲染树然后重新布局、绘制、合成。就像你拆了一面墙整个房子都得重新量尺寸。三、重绘只换皮肤不动骨架什么操作会触发重绘但不触发重排改变颜色color、background-color、border-color、box-shadow等。改变可见性visibility但display: none会触发重排。改变背景图、outline等。重绘的代价不需要重新布局但还是要重新绘制像素比重排轻但也不是免费。四、合成GPU加速的“超车道”合成是成本最低的环节因为它不涉及布局和绘制只把已有的图层合并。能触发合成的属性有transform平移、旋转、缩放opacityfilter当你用transform: translateZ(0)或will-change: transform时浏览器会把这个元素提升到单独的合成层后续动画只由GPU处理完全不触发重排和重绘。这就是为什么动画推荐用transform而不是left。/* 差触发重排 */.box{transition:left 0.3s;left:0;}.box:hover{left:100px;}/* 好只触发合成 */.box{transition:transform 0.3s;transform:translateX(0);}.box:hover{transform:translateX(100px);}五、如何减少重排和重绘1. 批量修改样式不要挨个改属性用class一次改完// 差element.style.width100px;element.style.height100px;element.style.margin10px;// 好element.classList.add(new-size);2. 让元素脱离文档流再操作比如要插入多个li可以先隐藏display: none改完再显示只触发两次重排。constuldocument.getElementById(list);ul.style.displaynone;for(leti0;i1000;i){constlidocument.createElement(li);li.textContenti;ul.appendChild(li);}ul.style.displayblock;3. 使用文档片段DocumentFragmentconstfragmentdocument.createDocumentFragment();for(leti0;i1000;i){constlidocument.createElement(li);li.textContenti;fragment.appendChild(li);}ul.appendChild(fragment);// 只触发一次重排4. 读写分离不要交替读取和修改布局属性否则会触发多次重排。// 差for(leti0;iboxes.length;i){boxes[i].style.widthboxes[i].offsetWidthpx;// 读后立即写}// 好先读后写constwidths[];for(leti0;iboxes.length;i){widths.push(boxes[i].offsetWidth);}for(leti0;iboxes.length;i){boxes[i].style.widthwidths[i]px;}5. 使用transform和opacity做动画永远不要用left、top、width、margin做动画改用transform。6. 固定元素位置position: fixed或absolute的元素其重排影响范围较小只在自己层内。7. 避免使用table布局一个小改动可能触发整个table的重排。六、实战一个性能优化的例子假设你要做一个跟随鼠标移动的小光点类似鼠标特效。错误做法每帧改变top/left触发重排。正确做法用transform。// 差每移动1px就重排一次dot.style.leftxpx;dot.style.topypx;// 好只触发合成dot.style.transformtranslate(${x}px,${y}px);七、怎么分析页面重排/重绘Chrome DevTools → Performance 面板录制一段操作查看“Layout Shift”、“Paint”等标记。红色紫色区域越少越好。八、总结三兄弟的“饭量”重排吃满汉全席最贵。动几何、DOM结构。重绘吃快餐中等。动颜色、背景。合成喝矿泉水几乎免费。动transform、opacity。优化口诀能用transform别用left能用class别改style读写分离批量操作。如果你觉得今天的“三兄弟”够形象点个赞让更多人看到。明天我们将聊聊JavaScript引擎与内存管理看看V8是怎么给代码“打扫卫生”的。我们明天见