面试题答案
一键面试重排(reflow)和重绘(repaint)原理
- 重排(reflow)原理:
- 重排也叫回流,当页面布局和几何属性(例如元素的尺寸、位置、隐藏/显示等)发生变化时,浏览器需要重新计算元素的几何属性,进而重新构建渲染树,这个过程就是重排。
- 浏览器渲染页面时,首先会构建文档对象模型(DOM)和CSS对象模型(CSSOM),然后将两者结合生成渲染树(render tree)。渲染树包含了页面中所有可见元素及其样式信息。重排发生时,渲染树的部分或全部会被重新计算和构建。例如,当改变元素的宽度,浏览器需要重新计算该元素及其子元素的位置和尺寸,可能还会影响到其他相邻元素的布局,导致整个渲染树部分甚至全部重新布局。
- 重绘(repaint)原理:
- 重绘是当元素的外观(例如颜色、背景色、边框样式等)发生变化,但不影响布局时,浏览器需要重新绘制受影响的部分,这个过程就是重绘。
- 重绘只涉及更新元素的外观,而不需要重新计算布局。例如,改变元素的颜色,浏览器只需在渲染树中找到该元素对应的节点,然后重新绘制其外观,而不会影响其他元素的布局。
频繁触发重排或重绘的CSS操作
- 频繁触发重排的操作:
- 改变元素的尺寸:例如修改
width
、height
、padding
、margin
、border-width
等属性。当这些属性改变时,元素的几何尺寸发生变化,会导致浏览器重新计算布局,触发重排。 - 改变元素的位置:如修改
top
、left
、bottom
、right
(对于定位元素),transform
属性的translate
部分(虽然transform
在一些情况下有性能优化,但改变translate
值仍会触发重排),这些操作改变了元素在页面中的位置,浏览器需要重新布局。 - 改变元素的字体大小:字体大小的改变会影响文本的布局,进而影响整个元素及其周围元素的布局,触发重排。
- 添加或删除可见的DOM元素:添加新元素会改变文档的结构,可能影响其他元素的布局;删除元素同样可能使周围元素重新排列,从而触发重排。
- 改变元素的尺寸:例如修改
- 频繁触发重绘的操作:
- 改变元素的颜色:例如修改
color
、background - color
等属性,元素的外观颜色改变,浏览器需要重新绘制该元素,触发重绘。 - 改变元素的边框样式:如修改
border - style
、border - color
等,元素的边框外观变化,导致重绘。 - 改变元素的透明度:修改
opacity
属性,元素的透明度发生变化,需要重新绘制以显示新的透明度效果,触发重绘。
- 改变元素的颜色:例如修改
优化以减少对性能的影响
- 批量操作:
- 尽量一次性修改元素的多个属性,而不是逐行修改。例如,不要多次分别修改
width
、height
和margin
,可以使用CSS类,一次性应用多个样式。
<style> .new - style { width: 200px; height: 100px; margin: 10px; } </style> <div id="myDiv"></div> <script> const div = document.getElementById('myDiv'); // 不好的方式,多次触发重排 div.style.width = '200px'; div.style.height = '100px'; div.style.margin = '10px'; // 好的方式,只触发一次重排 div.classList.add('new - style'); </script>
- 尽量一次性修改元素的多个属性,而不是逐行修改。例如,不要多次分别修改
- 使用
requestAnimationFrame
:- 当需要进行动画或连续的DOM操作时,使用
requestAnimationFrame
。它会在浏览器下一次重绘之前调用指定的回调函数,这样可以将多次操作合并到一次重排/重绘中。
function updateElement() { const div = document.getElementById('myDiv'); div.style.width = '300px'; div.style.height = '150px'; } requestAnimationFrame(updateElement);
- 当需要进行动画或连续的DOM操作时,使用
- 将元素脱离文档流:
- 在进行大量布局改变操作前,将元素设置为
position: absolute
或position: fixed
,使其脱离文档流,这样对该元素的操作不会影响其他元素的布局,从而减少重排范围。操作完成后再恢复其原来的定位。
<style> .myDiv { position: relative; } </style> <div id="myDiv" class="myDiv"></div> <script> const div = document.getElementById('myDiv'); // 脱离文档流 div.style.position = 'absolute'; // 进行大量布局操作 div.style.width = '400px'; div.style.height = '200px'; // 恢复原来定位 div.style.position ='relative'; </script>
- 在进行大量布局改变操作前,将元素设置为
- 使用
will - change
属性:- 在提前知道元素属性会发生变化时,使用
will - change
属性告知浏览器提前做好优化准备。例如,如果知道一个元素的transform
属性即将改变,可以提前设置will - change: transform
。但要注意不要滥用,因为它可能会占用额外的内存。
#myDiv { will - change: transform; }
- 在提前知道元素属性会发生变化时,使用