前端之重绘和重排
一、页面渲染的流程
解析html绘制DOM树
解析css绘制CSS树
把DOM和CSSOM组合生成render tree(渲染树)
在渲染树的基础上进行布局,计算每个节点的几何结构
把每个节点绘制到屏幕上(painting)
DOM发生改变的时候触发重排,使DOM重新排列,重绘不一定会重排,但重排一定会发生重绘,重绘和重排都会耗费浏览器的性能,尽量避免。
二、重排和重绘
- 重绘(repaint)意思是重新绘制:是在一个元素的外观被改变所触发的浏览器行为,浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。
引起重绘的属性和方法
- 当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如
- visibility、outline、背景色,改变字体颜色等属性的改变。
- 重排(reflow)意思是重新排列:当渲染树的一部分必须更新并且节点的尺寸发生了变化,浏览器会使渲染树中受到影响的部分失效,并重新构造渲染树。
引起重排的属性和方法--改变元素几何信息(大小和位置),都会引起重排
- 页面第一次渲染 在页面发生首次渲染的时候,所有组件都要进行首次布局,这是开销最大的一次回流。
- 浏览器窗口尺寸改变(resize事件发生时)
- 元素位置和元素的尺寸改变(外边距、内边距、边框厚度、宽高等几何属性)。
- 新增和删除可见元素。
- 内容改变(例如:文本改变或图片被另一个不同尺寸的图片替代、在input框输入内容)。
- 元素字体大小变化。
- 激活CSS伪类(例如::hover)。
- 设置style属性。
- 查询某些属性或调用某些方法。比如说:
- offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight。
- 除此之外,当我们调用getComputedStyle方法,或者IE里的currentStyle时,也会触发回流,原理是一样的,都为求一个“即时性”和“准确性”。
几何属性和布局信息的属性有哪些?
补充:几何属性包括布局、尺寸等可用数学几何衡量的属性:
布局:display、float、position、list、table、flex、columns、grid
尺寸:margin、padding、border、width、height
获取布局信息的属性或方法:
getComputedStyle()
getBoundingClientRect()
offsetTop、offsetLeft、offsetWidth、offsetHeight
scrollTop、scrollLeft、scrollWidth、scrollHeight
clientTop、clientLeft、clientWidth、clientHeight
三、减少reflow、repaint
- 不要一条一条的修改DOM的样式,可以先定义好css的class,然后修改DOM的className。
- 不要把DOM结点的属性值放在一个循环里当成循环里的变量。
- 为动画的HTML元件使用fixed或absolute的position,那么修改他们的css是不会reflow
四、建议
为了避免页面重排和重绘带来的性能问题,你可以采取以下措施:
- 尽量减少对 DOM 的操作,特别是对频繁操作的元素。
- 避免使用递归,尽量使用循环。
- 使用缓存,尽量避免重复计算。
- 使用 CSS3 的动画和过渡,避免使用 JavaScript 来控制动画。
- 尽量使用相对定位,避免使用绝对定位。
- 尽量使用轻量级的元素,如
div和span,避免使用重量级的元素,如table和form。 - 尽量减少对布局的修改,尤其是在页面加载后的修改,因为这会导致浏览器的重排。
- 使用 CSS 选择器的级别尽量低,因为浏览器在渲染时会从上到下匹配选择器,越深层的选择器匹配越慢,会导致更多的重排。
- 使用 CSS 预处理器,可以减少代码量,从而减少重排的次数。
- 使用计算属性或者函数计算样式,而不是直接使用字符串拼接的方式。
- 使用动画时,尽量使用 CSS3 动画,因为 CSS3 动画可以通过硬件加速来提升性能,而 JavaScript 动画则不行。
- 使用 requestAnimationFrame() 来控制动画的刷新率,这可以有效减少不必要的重绘。
- 使用 transform 属性来进行位移和缩放,因为 transform 属性会触发硬件加速,而 left、top 等属性则不会。
- 尽量避免使用 table 布局,因为 table 的布局会导致浏览器进行大量的重排。
为了避免过多的 DOM 操作,你可以使用以下方法:
- 使用文档片段(document fragment):文档片段是一个轻量级的容器,可以存储多个 DOM 元素。你可以先将所有要操作的元素放入文档片段中,然后再将文档片段添加到页面中。这样做可以减少对 DOM 的操作次数,从而提高性能。
- 使用 innerHTML:innerHTML 属性可以直接将一段 HTML 代码设置到元素中。你可以先使用字符串拼接的方式生成所有要操作的元素,然后再使用 innerHTML 将所有元素一次性添加到页面中。这样做可以减少对 DOM 的操作次数,从而提高性能。
- 使用缓存:在 JavaScript 中,你可以使用变量来缓存 DOM 元素。例如,你可以将常用的 DOM 元素存储在变量中,这样就可以直接使用变量而不必每次都去查找 DOM 元素。这样做可以减少对 DOM 的操作次数,从而提高性能。
- 使用事件委托:事件委托是指,将事件监听器添加到父元素上,而不是子元素。这样,当子元素触发事件时,父元素也会触发事件。这样做可以减少对 DOM 的操作次数,从而提高性能。
- 使用 CSS3 的动画和过渡:CSS3 的动画和过渡可以让你使用简单的代码实现复杂的动画效果。这些动画和过渡是由浏览器自动实现的,不会对 DOM 进行操作,因此可以大大提高性能。