重排和重绘
一、重绘(Repaint)
(一)定义
重绘是指当页面中某个元素的外观发生变化(但不改变其几何属性,如位置、尺寸等)时,浏览器重新绘制该元素的过程。
(二)触发条件
- 元素的背景色、颜色、字体等样式属性发生变化,例如将文字颜色从黑色改为红色。
- 元素的可见性(visibility)改变,例如从可见变为隐藏,或者相反。不过需要注意的是,visibility:hidden虽然会使元素不可见,但仍会为其保留空间,不影响页面布局;而display:none则是将元素完全从页面布局中移除。
- 元素的阴影(box-shadow、text-shadow等)发生变化,这会改变元素的视觉效果,但不改变其在页面布局中的位置和大小。
- 元素的透明度(opacity)改变,例如从完全不透明变为半透明状态。
(三)性能影响
- 重绘主要涉及元素外观的重新绘制,不涉及布局计算,相对重排来说性能开销较小。
- 但若大量元素频繁重绘,如动画效果中的每一帧都对多个元素进行外观修改,累积起来也会对性能产生较大影响,导致页面卡顿。
二、重排(Reflow)
(一)定义
重排也被称为回流,是当页面的布局发生变化(如元素的位置、尺寸或隐藏/显示状态改变等)时,浏览器重新计算页面的布局,并且通常会伴随着重绘的过程。
(二)触发条件
- DOM操作
- 添加、删除或修改可见的DOM元素。例如在页面中动态添加一个div元素,或者删除一个按钮元素,都会导致浏览器重新计算页面布局,因为这些操作改变了页面的结构。
- 改变元素的显示/隐藏状态,如通过display属性来控制元素的显示与隐藏。当display属性从none变为block,或者相反时,浏览器需要重新确定该元素在页面中的位置和大小。
- 几何属性改变
- 修改元素的位置(如left、top、right、bottom、margin、padding等属性变化)、尺寸(如width、height等属性变化)、边距、内边距、边框等几何属性。例如,将一个div元素的宽度从100px增加到200px,这不仅改变了该元素的大小,还可能影响其周围元素的布局。
- 内容变化
- 元素的内容发生变化,例如文本数量增多或减少、图片大小改变等。以文本内容为例,如果一段文字的长度变长,可能会导致包含该文字的元素宽度增加,进而影响父元素和其他兄弟元素的布局。
- 浏览器窗口大小变化
- 当用户调整浏览器窗口大小时,页面的布局需要重新适应新的窗口尺寸,从而触发重排。
- 其他情况
- 激活CSS伪类,如当鼠标悬停在元素上触发:hover伪类时,若伪类中定义了改变布局的样式(如改变元素的宽度),就会引起重排。
- 调用某些方法或查询某些属性,如offsetWidth、offsetHeight、getComputedStyle()等。这些操作会强制浏览器进行同步操作,导致重排。
(三)性能影响
- 重排的性能开销较大,因为需要重新计算页面布局,这涉及多个元素甚至整个页面的重新布局。
- 重排会触发重绘,而且一次重排可能会引发多次重绘。例如,当一个元素的大小改变时,它可能会影响其父元素和其他兄弟元素的布局,这些元素都需要重新计算位置和尺寸,然后重新绘制。
(四)重排示例
假设有一个简单的HTML页面布局如下:
<div class="container"><div class="box" id="box1"></div><div class="box" id="box2"></div>
</div>
CSS样式为:
.container {display: flex;height: 100px;
}.box {width: 100px;height: 100px;background-color: red;margin: 10px;
}
如果通过JavaScript改变第一个盒子元素(box1)的宽度:
document.getElementById('box1').style.width = '200px';
这时浏览器会进行重排:
- 首先,浏览器发现box1的宽度发生了变化。
- 由于box1和box2在同一个flex容器中,box1的宽度增加会影响它们在容器中的布局。
- 浏览器需要重新计算box1和box2的位置和尺寸,以及它们之间的间距(margin)等布局信息。
- 同时,容器元素(container)的布局也可能受到影响,需要重新确定其内部空间的分配。
- 最后,浏览器根据新的布局信息重新绘制这些元素。
三、重绘和重排的区别与联系
(一)区别
- 触发条件不同:重绘是由于元素外观改变(如颜色、阴影等)而触发;重排是由于元素布局改变(如大小、位置等)而触发。
- 性能开销不同:重排通常比重绘性能开销大,因为它涉及布局计算,并且可能引发重绘。
(二)联系
- 重排几乎总是会引发重绘,因为布局变化后,元素的外观呈现往往也需要更新。
- 重绘不一定会引起重排,但如果重绘的元素在布局上发生变化(例如通过改变font-size间接影响元素宽度),也可能导致重排。
四、优化重绘和重排的建议
(一)针对重绘的优化
- 减少不必要的样式变化:尽量避免频繁地修改元素的外观样式,特别是对于大量元素的动画效果,可以考虑使用CSS动画和过渡,它们通常比JavaScript操作样式更高效。
- 使用CSS类来修改样式:通过添加或移除CSS类来改变元素样式,而不是直接操作元素的style属性。这样可以更好地控制样式变化,并且有利于批量操作和样式管理。
- 延迟非关键的重绘操作:对于一些对用户体验影响不大的重绘操作,可以考虑延迟执行,例如在页面加载完成后或用户交互的空闲时间再进行。
(二)针对重排的优化
- 减少DOM操作:尽量减少对DOM元素的增删改操作,特别是批量操作时,可以先将元素从DOM中移除,进行修改后再重新插入。
- 使用文档片段(DocumentFragment):当需要向页面中添加大量DOM节点时,可以先将它们添加到文档片段中,最后再将文档片段统一插入文档中。这样可以减少重排次数,因为文档片段在内存中操作,不会立即触发重排。
- 避免频繁查询布局信息:避免在循环中频繁查询如offsetWidth、offsetHeight等布局信息,因为这会强制浏览器进行重排。可以将这些信息先存储起来,再进行后续操作。
- 使用CSS的will-change属性:可以提前告知浏览器哪些元素的属性将会发生变化,例如:
浏览器会根据这个提示进行一些性能优化,如提前分配资源等。.element {will-change: transform; }
- 避免使用table布局:table布局在重排时性能较差,因为表格的大小和单元格的大小相互依赖,修改一个单元格的大小可能会导致整个表格重新布局。尽量使用CSS Grid和Flex布局,它们提供了更灵活、高效的布局方式。
五、引起重排和重绘的属性
(一)引起重排的属性
引起重排的属性主要是与页面布局相关的属性,包括:
-
盒模型
display
: 改变元素的显示类型(如block
、inline
、flex
等)会影响元素的布局。padding
,margin
,width
,height
: 直接影响元素的尺寸和周围的空间。min-height
,max-height
,border
,border-width
: 改变这些属性会影响元素的尺寸和边框。
-
定位和浮动
position
: 改变元素的定位方式(如relative
、absolute
、fixed
等)会影响其在页面中的位置。top
,bottom
,left
,right
: 定位属性的改变会影响绝对或相对定位元素的位置。float
,clear
: 浮动属性的改变会影响元素的布局和周围元素的排列。
-
文字及溢出
font-family
,font-size
,font-weight
: 文本字体和大小的变化可能影响元素的宽度和高度。line-height
,text-align
,vertical-align
,white-space
: 这些属性的变化会影响文本在元素中的排列和元素的高度。overflow
,overflow-y
: 溢出属性的改变会影响元素的内容显示和滚动条的出现。
(二)引起重绘的属性
引起重绘的属性主要是与元素外观相关的属性,包括:
-
颜色
color
: 改变文本颜色。
-
边框
border-color
,border-style
,border-radius
: 改变边框的颜色、样式和圆角。
-
背景
background
,background-image
,background-position
,background-repeat
,background-size
: 改变背景的显示方式和位置。
-
轮廓
outline
,outline-color
,outline-style
,outline-width
: 改变轮廓的显示方式。
-
可见性
visibility
: 改变元素的可见性(如visible
、hidden
)。
-
文字方向
text-decoration
: 改变文本的装饰(如下划线、删除线)。
-
发光
box-shadow
: 改变元素的阴影效果。
六、避免重排和重绘的优化建议
(一)尽量使用仅引起合成的属性
- 使用
transform
和opacity
等属性进行动画,因为它们不会引起重排或重绘,而是通过合成来实现。
(二)限制重新渲染区域
- 使用
position: absolute
或position: fixed
等方法创建层叠上下文,减少重排的范围。 - 使用
contain: layout
或contain: paint
等属性值,使当前元素和内容独立于DOM树,减少重排的影响。
(三)减少使用display: table
或<table>
表格布局
- 表格布局在重排时性能较差,尽量使用CSS Grid和Flex布局。
(四)其他优化建议
- 减少DOM操作:尽量合并多次DOM操作,减少重排的次数。
- 使用文档片段(DocumentFragment):批量添加元素时,先将元素添加到文档片段中,再统一插入文档。
- 避免频繁查询布局信息:避免在循环中频繁查询
offsetWidth
、offsetHeight
等布局信息,可以将这些信息提前存储起来。