重绘和重排(回流)

一、浏览器渲染HTML的步骤

  1. HTML被HTML解析器解析成DOM Tree, CSS则被CSS解析器解析成CSSOM Tree。
  2. DOM Tree和CSSOM Tree解析完成后,被附加到一起,形成渲染树(Render Tree)。
  3. 节点信息计算(重排),这个过程被叫做Layout(Webkit)或者Reflow(Mozilla)。即根据渲染树计算每个节点的几何信息生成布局。
  4. 渲染绘制(重绘),这个过程被叫做(Painting 或者 Repaint)。即根据计算好的信息绘制整个页面。
  5. Display显示到屏幕上

以上5步简述浏览器的一次渲染过程,每一次的dom更改或者css几何属性更改,都会引起一次浏览器的重排/重绘过程,而如果是css的非几何属性更改,则只会引起重绘过程。所以说重排一定会引起重绘,而重绘不一定会引起重排重绘的开销较小,回流的代价较高。

二、重排(Relayout/Reflow)

所谓重排,实际上是根据渲染树中每个渲染对象的信息,计算出各自渲染对象的几何信息(DOM对象的位置和尺寸大小),并将其安置在界面中的正确位置。

重排也叫回流,实际上,reflow的字面意思也是回流,之所以有的叫做重排,也许是因为重排更好理解,更符合中国人的思维。标准文档之所以叫做回流(Reflow),是因为浏览器渲染是基于“流式布局”的模型,流实际就使我们常说的文档流,当dom或者css几何属性发生改变的时候,文档流会受到波动联动的去更改,流就好比一条河里的水,回流就好比向河里扔了一块石头,激起涟漪,然后引起周边水流受到波及,所以叫做回流,这样理解似乎更标准更规范,不过叫什么并不重要,重要的是我们真正理解了这个过程就行。

【1】重排的影响范围:

由于浏览器渲染界面是基于流式布局模型(Flow Based Layout),也就是某一个DOM节点信息更改了,就会触发重排。只是这个DOM更改程度会决定周边DOM更改范围,即全局范围和局部范围

  • 全局范围:就是从根节点html开始对整个渲染树进行重新布局,例如当我们改变了窗口尺寸或方向或者是修改了根元素的尺寸或者字体大小等;
  • 局部范围:对渲染树的某部分或某一个渲染对象进行重新布局。

【2】会引起重排的操作:

  • 页面首次渲染。
  • 浏览器窗口大小发生改变——resize事件发生时。
  • 元素尺寸或位置发生改变——定位、边距、填充、边框、宽度和高度。
  • 元素内容变化(文字数量或图片大小等等)。
  • 元素字体大小变化。
  • 添加或者删除可见的DOM元素。
  • 激活CSS伪类(例如::hover)。
  • 设置style属性
  • 查询某些属性或调用某些方法。

【3】常见引起重排属性和方法:

|

  • width
  • display
  • clientWidth
  • offsetWidth
  • scrollWidth
  • scrollIntoView()
  • getBoundingClientRect()

|

  • height
  • border
  • clientHeight
  • offsetHeight
  • scrollHeight
  • scrollTo()
  • scrollIntoViewIfNeeded()

|

  • margin
  • position
  • clientTop
  • offsetTop
  • scrollTop
  • getComputedStyle()

|

  • padding
  • overflow
  • clientLeft
  • offsetLeft
  • scrollLeft

|

三、重绘(Repainting)

相比重排,重绘就简单多了,所谓重绘,就是当页面中元素样式的改变并不影响它在文档流中的位置时,例如更改了字体颜色,浏览器会将新样式赋予给元素并重新绘制的过程称。

【1】常见引起浏览器绘制过程的属性包含:

|

  • color
  • text-decoration
  • outline-color
  • outline-width

|

  • border-style
  • background-image
  • outline
  • box-shadow

|

  • visibility
  • background-position
  • outline-style
  • background-size

|

  • background
  • background-repeat
  • border-radius

|

四、优化策略

其实浏览器很聪明,不可能每次修改样式就 reflow 或者 repaint 一次,一般来说,浏览器会积累一批操作,然后做一次 reflow。但是我们也应该减少重绘重排的次数。

我们知道操作DOM是一个高成本的操作,不仅是因为本身js与DOM的链接访问,还包括操作DOM后引起一连串的重排,因此,从性能优化角度,我们可以从以下几个方面着手:

【1】减少DOM操作

  • 最小化DOM访问次数,尽量缓存访问DOM的样式信息,避免过度触发回流。
  • 如果在一个局部方法中需要多次访问同一个dom,可以在第一次获取元素时用变量保存下来,减少遍历时间。
  • 用事件委托来减少事件处理器的数量。
  • 用querySelectorAll()替代getElementByXX()。

【2】减少重排

  • 避免设置大量的style内联属性,因为通过设置style属性改变结点样式的话,每一次设置都会触发一次reflow,所以最好是使用class属性
  • 不要使用table布局,因为table中某个元素一旦触发了reflow,那么整个table的元素都会触发reflow。那么在不得已使用table的场合,可以设置table-layout:auto;或者是table-layout:fixed这样可以让table一行一行的渲染,这种做法也是为了限制reflow的影响范围
  • 尽量少使用display:none可以使用visibility:hidden代替,display:none会造成重排,visibility:hidden只会造成重绘
  • 使用resize事件时,做防抖节流处理。

【3】css及优化动画

  • 少用css表达式
  • 减少通过JavaScript代码修改元素样式,尽量使用修改class名方式操作样式或动画;
  • 可以把动画效果应用到position属性为absolute或fixed的元素上,这样对其他元素影响较小
  • 动画实现的速度的选择。比如实现一个动画,以1个像素为单位移动这样最平滑,但是reflow就会过于频繁,大量消耗CPU资源,如果以3个像素为单位移动则会好很多。
  • 开启动画的GPU加速,把渲染计算交给GPU。

你可能感兴趣的:(重绘和重排(回流))