前端之重绘和重排

218 阅读5分钟

前端之重绘和重排

一、页面渲染的流程

    解析html绘制DOM树

    解析css绘制CSS树

    把DOM和CSSOM组合生成render tree(渲染树)

    在渲染树的基础上进行布局,计算每个节点的几何结构

    把每个节点绘制到屏幕上(painting)

DOM发生改变的时候触发重排,使DOM重新排列,重绘不一定会重排,但重排一定会发生重绘,重绘和重排都会耗费浏览器的性能,尽量避免。

二、重排和重绘

  • 重绘(repaint)意思是重新绘制:是在一个元素的外观被改变所触发的浏览器行为,浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。

引起重绘的属性和方法

  1. 当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如
  2. visibility、outline、背景色,改变字体颜色等属性的改变。
  • 重排(reflow)意思是重新排列:当渲染树的一部分必须更新并且节点的尺寸发生了变化,浏览器会使渲染树中受到影响的部分失效,并重新构造渲染树。

引起重排的属性和方法--改变元素几何信息(大小和位置),都会引起重排

  1. 页面第一次渲染 在页面发生首次渲染的时候,所有组件都要进行首次布局,这是开销最大的一次回流。
  2. 浏览器窗口尺寸改变(resize事件发生时)
  3. 元素位置和元素的尺寸改变(外边距、内边距、边框厚度、宽高等几何属性)。
  4. 新增和删除可见元素。
  5. 内容改变(例如:文本改变或图片被另一个不同尺寸的图片替代、在input框输入内容)。
  6. 元素字体大小变化。
  7. 激活CSS伪类(例如::hover)。
  8. 设置style属性。
  9. 查询某些属性或调用某些方法。比如说:
  10. offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight。
  11. 除此之外,当我们调用getComputedStyle方法,或者IE里的currentStyle时,也会触发回流,原理是一样的,都为求一个“即时性”和“准确性”。

几何属性和布局信息的属性有哪些?

补充:几何属性包括布局、尺寸等可用数学几何衡量的属性:

    布局:displayfloatposition、list、tableflexcolumnsgrid

    尺寸:marginpaddingborderwidthheight

获取布局信息的属性或方法:

    getComputedStyle()

    getBoundingClientRect()
    
    offsetTop、offsetLeft、offsetWidth、offsetHeight

    scrollTop、scrollLeft、scrollWidth、scrollHeight

    clientTop、clientLeft、clientWidth、clientHeight

三、减少reflow、repaint

  1. 不要一条一条的修改DOM的样式,可以先定义好css的class,然后修改DOM的className。
  2. 不要把DOM结点的属性值放在一个循环里当成循环里的变量。
  3. 为动画的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 操作,你可以使用以下方法:

  1. 使用文档片段(document fragment):文档片段是一个轻量级的容器,可以存储多个 DOM 元素。你可以先将所有要操作的元素放入文档片段中,然后再将文档片段添加到页面中。这样做可以减少对 DOM 的操作次数,从而提高性能。
  2. 使用 innerHTML:innerHTML 属性可以直接将一段 HTML 代码设置到元素中。你可以先使用字符串拼接的方式生成所有要操作的元素,然后再使用 innerHTML 将所有元素一次性添加到页面中。这样做可以减少对 DOM 的操作次数,从而提高性能。
  3. 使用缓存:在 JavaScript 中,你可以使用变量来缓存 DOM 元素。例如,你可以将常用的 DOM 元素存储在变量中,这样就可以直接使用变量而不必每次都去查找 DOM 元素。这样做可以减少对 DOM 的操作次数,从而提高性能。
  4. 使用事件委托:事件委托是指,将事件监听器添加到父元素上,而不是子元素。这样,当子元素触发事件时,父元素也会触发事件。这样做可以减少对 DOM 的操作次数,从而提高性能。
  5. 使用 CSS3 的动画和过渡:CSS3 的动画和过渡可以让你使用简单的代码实现复杂的动画效果。这些动画和过渡是由浏览器自动实现的,不会对 DOM 进行操作,因此可以大大提高性能。