浏览器渲染及相关性能

一、浏览器渲染

1、网页的生成过程

1.解析HTML/XHML/SVG生成DOM树

2.解析css生成CSSOM (css规则树)

3.结合DOM和CSSOM,生成一棵渲染树(Render Tree)(包含每个节点的视觉信息)。

  Rendering Tree 渲染树并不等同于DOM树,因为一些像Header或display:none的东西就没必要放在渲染树中了。

  CSS 的 Rule Tree主要是为了完成匹配并把CSS Rule附加上Rendering Tree上的每个Element。

  CSS匹配HTML元素是一个相当复杂和有性能问题的事情。所以,你就会在N多地方看到很多人都告诉你,DOM树要小,CSS尽量用id和class,千万不要过渡层叠下去,……

4.生成布局(layout),计算元素大小、节点位置。即将所有渲染树的所有节点进行平面合成。

5.最后通过调用操作系统Native GUI的API绘制。

2.重排(reflow)和 重绘(repaint)

  • Repaint——屏幕的一部分要重画,比如某个CSS的背景色变了。但是元素的几何尺寸没有变。
  • Reflow——意味着元件的几何尺寸变了,我们需要重新验证并计算Render Tree。是Render Tree的一部分或全部发生了变化。这就是Reflow,或是Layout。(HTML使用的是flow based layout,也就是流式布局,所以,如果某元件的几何尺寸发生了变化,需要重新布局,也就叫reflow )reflow 会从<html>这个root frame开始递归往下,依次计算所有的结点几何尺寸和位置,在reflow过程中,可能会增加一些frame,比如一个文本字符串必需被包装起来。

三、对于性能的影响

当你增加、删除、修改DOM结点时,会导致Reflow或Repaint

  • 当你移动DOM的位置,或是搞个动画的时候。
  • 当你修改CSS样式的时候。
  • 当你Resize窗口的时候(移动端没有这个问题),或是滚动的时候。
  • 当你修改网页的默认字体时。
  • 注:display:none会触发reflow,而visibility:hidden只会触发repaint,因为没有发现位置变化。

    多说两句关于滚屏的事,通常来说,如果在滚屏的时候,我们的页面上的所有的像素都会跟着滚动,那么性能上没什么问题,因为我们的显卡对于这种把全屏像素往上往下移的算法是很快。但是如果你有一个fixed的背景图,或是有些Element不跟着滚动,有些Elment是动画,那么这个滚动的动作对于浏览器来说会是相当相当痛苦的一个过程。你可以看到很多这样的网页在滚动的时候性能有多差。因为滚屏也有可能会造成reflow。

    基本上来说,reflow有如下的几个原因:

    • Initial。网页初始化的时候。
    • Incremental。一些Javascript在操作DOM Tree时。
    • Resize。其些元件的尺寸变了。
    • StyleChange。如果CSS的属性发生变化了。
    • Dirty。几个Incremental的reflow发生在同一个frame的子树上。

四、提高性能的九个技巧

第一条,DOM 的多个读操作(或多个写操作),应该放在一起。不要两个读操作之间,加入一个写操作。

一般来说,浏览器会把这样的操作积攒一批,然后做一次reflow,这又叫异步reflow或增量异步reflow 。但是有些情况浏览器是不会这么做的,比如:resize窗口,改变了页面默认的字体,等。对于这些操作,浏览器会马上进行reflow。

但是有些时候,我们的脚本会阻止浏览器这么干,比如:如果我们请求下面的一些DOM值:

  1. offsetTop, offsetLeft, offsetWidth, offsetHeight
  2. scrollTop/Left/Width/Height
  3. clientTop/Left/Width/Height
  4. IE中的 getComputedStyle(), 或 currentStyle

因为,如果我们的程序需要这些值,那么浏览器需要返回最新的值,而这样一样会flush出去一些样式的改变,从而造成频繁的reflow/repaint。

第二条,如果某个样式是通过重排得到的,那么最好缓存结果。避免下一次用到的时候,浏览器又要重排。

第三条,不要一条条地改变样式,而要通过改变class,或者csstext属性,一次性地改变样式。

第四条,尽量使用离线DOM,而不是真实的网面DOM,来改变元素样式。比如,操作Document Fragment对象,完成后再把这个对象加入DOM。再比如,使用 cloneNode() 方法,在克隆的节点上进行操作,然后再用克隆的节点替换原始节点。

第五条,先将元素设为display: none(需要1次重排和重绘),然后对这个节点进行100次操作,最后再恢复显示(需要1次重排和重绘)。这样一来,你就用两次重新渲染,取代了可能高达100次的重新渲染。

第六条,position属性为absolutefixed的元素,重排的开销会比较小,因为不用考虑它对其他元素的影响。

第七条,只在必要的时候,才将元素的display属性为可见,因为不可见的元素不影响重排和重绘。另外,visibility : hidden的元素只对重绘有影响,不影响重排。

第八条,使用虚拟DOM的脚本库,比如React等。

第九条,使用 window.requestAnimationFrame()、window.requestIdleCallback() 这两个方法调节重新渲染

原文地址:https://www.cnblogs.com/xinup/p/4876180.html