- 浏览器渲染HTML的过程大致可以分为4步。
- HTML文件被HTML解析器解析成对应的DOM树,CSS样式文件被CSS解析器解析生成对应的样式规则集。
- DOM树与CSS样式集解析完成后,附加在一起形成一个渲染树。
- 节点信息的计算,即根据渲染树计算每个节点的几何信息。
- 渲染绘制,即根据计算完成的节点信息绘制整个页面。
- 重排和重绘发生在第3步和第4步中
重排
- 重排是一种明显的改变页面布局的操作,下面总结了常见的引起重排的操作。
- 页面首次渲染。
- 在页面首次渲染时,HTML页面的各个元素位置、尺寸、大小等信息均是未知的,需要通过与CSS样式规则集才能确定各个元素的几何信息。这个过程中会产生很多元素几何信息计算的过程,所以会产生重排操作。
- 浏览器窗口大小发生改变。
- 页面渲染完成后,就会得到一个固定的渲染树。如果此时对浏览器窗口进行缩放或者拉伸操作,渲染树中从根元素html标签开始的所有元素,都会重新计算其几何信息,从而产生重排操作。
- 元素尺寸或位置发生改变。
- 元素内容发生变化。
- 元素字体发生变化。
- 上述3种情况,均是直观地表述DOM元素几何属性的变化。这些操作均会导致渲染树中相关的节点失效,浏览器会根据DOM元素的变化,重新构建渲染树中失效的节点,从而产生重排操作。
- 添加或删除可见的DOM元素。
- 因为浏览器采用的是流式布局模型,实际为从上到下、从左到右依次遍历元素的过程。通常情况下,如果添加或者删除可见的DOM元素,则当前元素之前的元素不会受到影响;而当前元素之后的元素均会重新计算几何信息,渲染树也需要重新构建修改后的节点,从而产生重排操作
- 获取某些特定的属性。
- 也许几行简单的JavaScript代码就会引起很多重排的操作,而频繁的重排操作会对浏览器引擎产生很大的消耗。所以浏览器不会针对每个JS操作都进行一次重排,而是维护一个会引起重排操作的队列,等队列中的操作达到了一定的数量或者到了一定的时间间隔时,浏览器才会去flush一次队列,进行真正的重排操作。
- 页面首次渲染。
当我们请求以上这些属性时,浏览器为了返回最精准的信息,需要flush队列,因为队列中的某些操作可能会影响到某些值的获取。因此,即使你获取的样式信息与队列中的操作无关,浏览器仍然会强制flush队列,从而引起浏览器重排的操作。
- 在获取以下一些常见的属性和函数时,会引发重排的操作。
- width:宽度。
- height:高度。
- margin:外边距。
- padding:内边距。
- display:元素显示方式。
- border:边框。
- position:元素定位方式。
- overflow:元素溢出处理方式。
- clientWidth:元素可视区宽度。
- clientHeight:元素可视区高度。
- clientLeft:元素边框宽度。
- clientTop:元素边框高度。
- offsetWidth:元素水平方向占据的宽度。
- offsetHeight:元素水平方向占据的高度。
- offsetLeft:元素左外边框至父元素左内边框的距离。
- offsetTop:元素上外边框至父元素上内边框的距离。
- scrollWidth:元素内容占据的宽度。
- scrollHeight:元素内容占据的高度。
- scrollLeft:元素横向滚动的距离。
- scrollTop:元素纵向滚动的距离。
- scrollIntoView():元素滚动至可视区的函数。
- scrollTo():元素滚动至指定坐标的函数。
- getComputedStyle():获取元素的CSS样式的函数。
- getBoundingClientRect():获取元素相对于视窗的位置集合的函数。
- scrollIntoViewIfNeeded():元素滚动至浏览器窗口可视区的函数。(非标准特性,谨慎使用)
重绘
:::info
重绘只是改变元素在页面中的展现样式,而不会引起元素在文档流中位置的改变。例如更改了元素的字体颜色、背景色、透明度等,浏览器均会将这些新样式赋予元素并重新绘制。
:::
在修改某些常见的属性时,会引发重绘的操作,接下来列举出了一部分。
· color:颜色。
· border-style:边框样式。
· visibility:元素是否可见。
· background:元素背景样式,包括背景色、背景图、背景图尺寸、背景图位置等。
· text-decoration:文本装饰,包括文本加下画线、上划线、贯穿线等。
· outline:元素的外轮廓的样式,在边框外的位置。
· border-radius:边框圆角。
· box-shadow:元素的阴影。
在经过上文的讲解后,相信大家都对重排与重绘的操作已经有所了解,那么它们之间有什么关系呢?简单来说,重排一定会引起重绘的操作,而重绘却不一定会引起重排的操作。因为在元素重排的过程中,元素的位置等几何信息会重新计算,并会引起元素的重新渲染,这就会产生重绘的操作。而在重绘时,只是改变了元素的展现样式,而不会引起元素在文档流中位置的改变,所以并不会引起重排的操作。
性能优化
- 浏览器的重排与重绘是比较消耗性能的操作,所以我们应该尽量地减少重排与重绘的操作,这也是优化网页性能的一种方式。那么具体来说我们可以怎么做呢?
- 将多次改变样式的属性操作合并为一次
- 将需要多次重排的元素设置为绝对定位
- 在内存中多次操作节点,完成后再添加至文档树中
- 将要进行复杂处理的元素处理为display属性为none,处理完成后再进行显示
- 将频繁获取会引起重排的属性缓存至变量
- 尽量减少使用table布局
- 使用事件委托绑定事件处理程序
- 利用DocumentFragment操作DOM节点