渲染和重排

119 阅读5分钟

1浏览器渲染的过程

  1. 根据html文件构建DOM树和CSSOM树。构建DOM树期间,如果遇到JS,阻塞DOM树及CSSOM树的构建,优先加载JS文件,加载完毕,再继续构建DOM树及CSSOM树。
  2. 构建渲染树(render Tree)。
  3. 页面的重绘(repaint)与重排/回流(reflow)。页面渲染完成后,若JS操作了DOM节点,根据JS对DOM操作动作的大小,浏览器对页面进行重绘或是重排。
  • 回流:某些元素的外观被改变,例如:元素的填充颜色。
  • 重绘:重新生成布局,重新排列元素。当DOM的变化影响了元素的几何信息(元素的的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做重排。

根据 Opera 的说法,重绘是一项昂贵的操作,因为它迫使浏览器验证/检查所有其他 dom 节点的可见性。

2减少重排的传统方法

1.样式集中改变,如使用classname

2.Batch DOM changes and perform them "offline"(脱机). Offline means not in the live DOM tree. You can:

  • use a documentFragment to hold temp changes

在使用JavaScript来操作DOM元素时,比如使用appendChild()方法。每次调用该方法时,浏览器都会重新渲染页面。如果大量的更新DOM节点,则会非常消耗性能,影响用户体验。 JavaScript提供了一个文档片段DocumentFragment的机制。如果将文档中的节点添加到文档片段中,就会从文档树中移除该节点。把所有要构造的节点都放在文档片段中执行,这样可以不影响文档树,也就不会造成页面渲染。当节点都构造完成后,再将文档片段对象添加到页面中,这时所有的节点都会一次性渲染出来,这样就能减少浏览器负担,提高页面渲染速度。(实际上还是一种批量修改DOM的方法)

  • clone the node you're about to update, work on the copy, then swap the original with the updated clone
  • display: none

3.讲计算属性设置成变量在访问

如:var left = el.offsetLeft

4.使用绝对定位脱离文档流

absolute 或 fixed

5.其他优化

虽然修改元素的几何属性会导致浏览器触发重排或重绘时。但现代浏览器优化了这个过程,它会把这些操作放进渲染队列,等到队列中的操作到了一定的数量或者到了一定的时间间隔时,浏览器就会批量执行(flush)这些操作。

但有些操作会引起浏览器提前flush队列,比如,当我们向浏览器请求以下style信息时,就会提前让浏览器flush队列:

offsetTop,offsetLeft,offsetWidth,offsetHeight
scrollTop/Left/Width/Height
clientTop/Left/Width/Height
width,height
请求了getComputedStyle()或者IE的currentStyle

原因: 请求以上这些值时,浏览器需要清空队列,计算出最新的元素尺寸和位置样式信息(重绘回流),因为浏览器认为队列中的某些操作会造成我们获取的值并不是最精确的!

3.是什么让 React 的虚拟 DOM 如此之快?

React 并没有真正做任何新的事情。这只是一个战略举措。它的作用是将真实 DOM 的副本存储在内存中。当您修改 DOM 时,它首先将这些更改应用到内存中的 DOM。然后,使用它的差异算法,找出真正发生了什么变化。

最后,它对更改进行批处理,并调用一次将它们应用到 real-dom 上。因此,最大限度地减少了回流和重绘。

requestAnimationFrame

网页的动画效果主要有两种实现方式:

CSS3动画

– CSS是关键帧动画,补间动画部分由浏览器完成,便于浏览器进行优化,可以更好控制动画执行过程 – CSS的动画执行在合成线程,专事专干,不阻塞主线程,合成线程的动画也不会触发回流和重绘 – CSS动画允许在GPU,专注渲染,更快(复合图层的概念,后面会提到)

JS动画

– JS是逐帧动画,每一帧都是由代码控制,操作不当,极易引发回流 – JS的动画执行在主线程,主线程还有其他任务要执行,容易引发阻塞和等待,降低动画执行效率 – JS动画运行在CPU,但CPU还有其他任务,易受影响 既然JS动画看上去这么拉,为什么要使用JS动画?因为可以通过编程实现复杂的动画效果。通常会使用setTimeout或者setInterval,以及requestAnimationFrame。

然而相比于setTimeout和setInterval而言,requestAnimationFrame有两个优点:

requestAnimationFrame 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧。 在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的的CPU、GPU和内存使用量。

Canvas会触发重绘和重排吗?

除非Canvas本身的位置或者大小发生变化,影响了render tree,才会发生重排和回流。

理由是:重绘和回流都是相对于render tree上的元素而言的,而对canvas本身进行绘制并未对页面任何其他元素做更改,故只会引起Canvas画布本身的重绘。