你有没有试过,在办公室用手机连着WiFi打开公司内部系统,点个按钮半天没反应,刷新一下页面又卡顿几秒?别急着怪路由器——有时候,锅在HTML里。
重排(reflow)不是修图,是浏览器重新算布局
当你改了某个元素的宽度、高度、位置、字体大小,或者增删了DOM节点,浏览器就得重新计算所有相关元素在页面上的几何位置和大小。这个过程叫重排。它代价很高,尤其在中低端手机上,一次重排可能让页面“顿”一下,就像WiFi信号突然被遮挡那样——没断,但就是卡。
比如这段代码:
<div id="list"></div>
<script>
for (let i = 0; i < 1000; i++) {
document.getElementById('list').innerHTML += '<li>第' + i + '项</li>';
}
</script>每次拼接字符串再赋值 innerHTML,浏览器都得重排整个 list 容器——1000 次,就是 1000 次重排。换成 WiFi 覆盖场景:你正用平板看园区AP分布热力图,每加一个AP标记就触发一次重排,地图拖动立马变幻灯片。重绘(repaint)看着轻,攒多了也压垮渲染线程
只改颜色、背景、阴影这些不改变布局的样式,浏览器跳过重排,只重绘像素。听起来轻松?但重绘要走GPU上传、合成层绘制、帧缓冲输出一整套流程。如果连续触发(比如用 setTimeout 频繁改 background-color),渲染线程忙不过来,页面就跟WiFi视频通话掉帧一样——画面撕裂、文字模糊、按钮点击无响应。
更隐蔽的是“隐式重排”:读取 offsetHeight、getComputedStyle() 这类属性时,浏览器若发现布局已过期,会立刻同步执行一次重排。很多人写动画时一边改样式一边读尺寸,等于自己给浏览器下指令:“刚干完活,马上重来一遍”。
怎么绕开?几个WiFi现场能用的招
• 批量操作DOM:用 documentFragment 先组装好节点,再一次性挂到页面上;
• 开启硬件加速:对频繁动画的元素加 transform: translateZ(0) 或 will-change: transform,让它进独立合成层,重绘不牵连其他区域;
• 避免强制同步:把读尺寸和写样式拆成两个任务,用 requestAnimationFrame 错开执行时机。
下次巡检WiFi覆盖效果页面,发现热力图加载慢、AP列表滚动卡顿,不妨打开开发者工具的“Rendering”面板,勾上“Paint flashing”,点几下页面——闪红块的地方,八成就是重绘重排在偷偷吃带宽和CPU。