webkit渲染,以及重排重绘

webkit 包含两部分引擎组件:WebCore 和 JavaScriptCore;

Safari,Amazon Kindle e-book reader,BlackBerry,Android(4.4之前) 使用的webkit;
Chrome 和 opera、Android(4.4开始)从2013年开始使用 Blink(webkit 的 webCore 和 JavaScript 引擎 V8);
浏览器引擎对比可参考 Comparison of web browser engines
浏览器对比参考 Comparison of web browsers

webkit一般性渲染过程

 根据渲染引擎所提供的渲染网页的功能,主要分三层,最上层用虚线框住的是渲染引擎所提供的功能。

       这些模块依赖很多其他的基础模块,这其中包括网络、存储、2D/3D图形、音频视频和图片解码器等。实际上,渲染引擎中还应该包括如何使用这些依赖模块的部分。

虚线——是每个阶段可能使用到的其它模块:
        网页内容的下载中——使用到网络和存储;
        计算布局和绘图的时候,需要使用2D/3D的图形模块,还需要开始解码音频、视频和图片,同其他内容一起绘制到最后的图像中。
       在渲染完成之后,用户可能需要跟渲染的结果进行交互,或者网页自身有动画操作,一般而言,这需要持续的重复渲染过程。
根据数据的流向,这里将渲染过程分成三个阶段,
    第一个阶段是从 网页URL 到构建完 DOM 树,
    第二个阶段是从 DOM树 到构建完 WebKit 的绘图上下文,
    第三个阶段是从 绘图上下文 到生成最终的 图像。
      图中描述的是从网页URL到构建完DOM树的这个过程,数字表示的是基本顺序,当然也不是严格一致,因为这个过程可能重复并且可能交叉。
    具体的过程如下。
    1. 当用户输入网页URL的时候,WebKit 调用其资源加载器加载该URL对应的网页。
    2. 加载器依赖网络模块建立连接,发送请求并接收答复。
    3. WebKit 接收到各种网页或者资源的数据,其中某些资源可能是同步或异步获取的。
    4. 网页被交给 HTML解释器转变成一系列的词语(Token)。
    5. 解释器根据词语构建节点(Node),形成DOM树。
    6. 如果节点是 JavaScript 代码的话,调用 JavaScript 引擎解释并执行。
    7. JavaScript 代码可能会修改 DOM 树的结构
    8. 如果节点需要依赖其他资源,例如图片、CSS、视频等,调用资源加载器来加载它们,但是它们是异步的,不会阻碍当前DOM树的继续创建;如果是JavaScript资源URL(没有标记异步方式),则需要停止当前DOM树的创建,直到JavaScript的资源加载并被JavaScript引擎执行后才继续DOM树的创建。
    在上述的过程中,网页在加载和渲染过程中会发出“DOMContent”事件和DOM的”onload”事件,分别在DOM树构建完成之后,以及DOM树建完,并且网页所依赖的资源都加载完成之后发生,因为某些资源的加载并不会阻碍DOM树的创建,所以这两个事件多数时候不是同时发生的。
    接下来就是 WebKit 利用 CSS 和 DOM树 构建 RenderObject树 直到 绘图上下文
    这一阶段的具体过程如下:
    1. css文件被 css解释器解释成内部表示结构
    2. CSS解释器工作完之后,在 DOM树上附加解释后的样式信息,这就是 RenderObject树
    3. RenderObject节点 在创建的同时,WebKit 会根据网页的层次结构创建 RenderLayer树,同时构建一个虚拟的绘图上下文。
    RenderObject树的建立并不表示DOM树会被销毁,事实上,上述图中的四个内部表示结构一直存在,直到网页被销毁,因为它们对于网页的渲染起了非常大的作用。
    最后就是根据绘图上下文生成最终的图像,这一过程主要依赖2D和3D图形库,如下图:
    图中这一阶段对应的具体过程如下:
1. 绘图上下文是一个与平台无关的抽象类,它将每个绘图操作桥接到不同的具体实现类,也就是绘图具体实现类。
2. 绘图实现类也可能有简单的实现,也可能有复杂的实现。
3. 绘图实现类将2D图形库或者3D图形库绘制的结果保存下来,交给浏览器来同浏览器界面一起显示。

重绘和重排

  1. 重排(reflow):部分渲染树(或者整个渲染树)需要重新分析并且节点尺寸需要重新计算。至少会有一次重排-初始化页面布局。
  2. 重绘(repaint):节点的几何属性或者样式发生改变,例如改变元素背景色,屏幕上的部分内容需要更新。

什么情况会触发重排和重绘?

以下三种情况,会导致网页重新渲染,就需要重新生成布局和重新绘制。

  • 添加、删除、更新DOM节点
  • 通过display: none隐藏一个DOM节点-触发重排和重绘
  • 通过visibility: hidden隐藏一个DOM节点-只触发重绘,因为没有几何变化
  • 移动或者给页面中的DOM节点添加动画
  • 添加一个样式表,调整样式属性
  • 用户行为,例如调整窗口大小,改变字号,鼠标悬停、页面滚动、输入框键入文字。

样式的写操作之后,如果有下面这些属性的读操作,都会引发浏览器立即重新渲染。

offsetTop/offsetLeft/offsetWidth/offsetHeight
scrollTop/scrollLeft/scrollWidth/scrollHeight
clientTop/clientLeft/clientWidth/clientHeight
getComputedStyle()

所以,从性能角度考虑,尽量不要把读操作和写操作,放在一个语句里面。

一般的规则是:
样式表越简单,重排和重绘就越快。
重排和重绘的DOM元素层级越高,成本就越高。
table元素的重排和重绘成本,要高于div元素

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

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

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

// bad
var left=10;
var top=10;
el.style.left= left+”px”;
el.style.top= top+”px”;
// good
el.className+=” theclassname”;
// good
el.style.cssText+=”;
left: “+ left+”px;
top: “+ top+”px;”;

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

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

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

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

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

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

参考:
http://zhangzhaoaaa.iteye.com/blog/2183520?utm_source=tuicool&utm_medium=referral
http://www.cnblogs.com/Chen-XiaoJun/p/6209821.html
http://www.jianshu.com/p/3259ed7f68e2
https://en.wikipedia.org/wiki/WebKit

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注