分享

详解浏览器渲染页面的过程

 昵称43682464 2018-03-07

当浏览器拿到一个网页是如何进行渲染的呢?
首先浏览器渲染页面前需要先构建 DOM 和 CSSOM 树。因此,我们需要确保尽快将 HTML 和 CSS 都提供给浏览器。
下面来看浏览器绘制DOM 树的过程:

HTML解析器输出的树是由DOM元素和属性节点组成的,它是HTML文档的对象化描述,也是HTML元素与外界(如Javascript)的接口。DOM与标签有着几乎一一对应的关系。
这里写图片描述

可以看到DOM树的构建过程为:将字节转换成字符,确定tokens,将tokens转换成节点,然后构建 DOM 树。

在浏览器构建这个简单页面的 DOM 过程中,在文档的 head 中遇到了一个 link 标记,该标记引用一个外部 CSS 样式表:style.css。由于预见到需要利用该资源来渲染页面,它会立即发出对该资源的请求。并进行CSSDOM树的构建。

这里写图片描述

CSSOM 为何具有树结构?为页面上的任何对象计算最后一组样式时,浏览器都会先从适用于该节点的最通用规则开始(例如,如果该节点是 body 元素的子项,则应用所有 body 样式),然后通过应用更具体的规则(即规则“向下级联”)以递归方式优化计算的样式。

以上面的 CSSOM 树为例进行更具体的阐述。span 标记内包含的任何置于 body 元素内的文本都将具有 16 像素字号,并且颜色为红色 — font-size 指令从 body 向下级联至 span。不过,如果某个 span 标记是某个段落 (p) 标记的子项,则其内容将不会显示。
CSSOM 树和 DOM 树合并成渲染树,然后用于计算每个可见元素的布局,并输出给绘制流程,将像素渲染到屏幕上。绘制过程如下:

>

  • DOM 树与 CSSOM树合并后形成渲染树,它只包含渲染网页所需的节点。遍历每个DOM树中的node节点,在CSSOM规则树中寻找当前节点的样式,生成渲染树。
  • 布局计算每个对象的精确位置和大小。
  • 最后一步是绘制,使用最终渲染树将像素渲染到屏幕上。

那么当资源中还有js时会发生什么?答案是阻塞DOM树的构建!
脚本在文档的何处插入,就在何处执行。当 HTML 解析器遇到一个 script 标记时,它会暂停构建 DOM,将控制权移交给 JavaScript 引擎;等 JavaScript 引擎运行完毕,浏览器会从中断的地方恢复 DOM 构建。而因此就会推迟页面首绘的时间。

提到首绘这里来提两个概念:DOMContentLoaded和load

1.当 onload 事件触发时,页面上所有的DOM,样式表,脚本,图片,flash都已经加载完成了。
2.当 DOMContentLoaded 事件触发时,仅当DOM加载完成,不包括样式表,图片,flash。

页面的首次绘制是在渲染树解析构建完毕之后。因此所需的关键资源是构建DOM树和CSSDOM的html和css。但是当执行js时会阻塞html的解析也就推迟了页面的首绘时间。该如何解决这个问题呢?
可以在首绘不需要js的情况下用async和defer实现异步加载。这样就js就不会阻塞html的解析了。注意,异步执行是指下载。执行js时仍然会阻塞。

script async src='siteScript.js' onload='myInit()'>script> script defer src='siteScript.js' onload='myInit()'>script>
  • 1
  • 2
  • 3
  • 4

以上两种方式都可以实现js的异步加载。那么两者之间有什么区别呢?

  • async 脚本在script文件下载完成后会立即执行,并且其执行时间一定在 window的load事件触发之前。这意味着多个async脚本很可能不会按其在页面中的出现次序顺序执行。
  • 与此相对,浏览器确保多个 defer 脚本按其在HTML页面中的出现顺序依次执行,且执行时机为DOM解析完成后,document的DOMContentLoaded 事件触发之前。

由于async脚本执行顺序的不确定性因此不能用来加载具有明显依赖关系的js会造成乱序。

DOM树表示页面结构,渲染树表示DOM节点如何显示。DOM树中的每一个需要显示的节点在渲染树种至少存在一个对应的节点(隐藏的DOM元素disply值为none 在渲染树中没有对应的节点)。渲染树中的节点被称为“帧”或“盒”,符合CSS模型的定义,理解页面元素为一个具有填充,边距,边框和位置的盒子。一旦DOM和渲染树构建完成,浏览器就开始显示(绘制)页面元素。

那么什么是重排和重绘呢
当DOM的变化影响了元素的几何属性(宽或高),浏览器需要重新计算元素的几何属性,同样其他元素的几何属性和位置也会因此受到影响。浏览器会使渲染树中受到影响的部分失效,并重新构造渲染树。这个过程称为重排。完成重排后,浏览器会重新绘制受影响的部分到屏幕,该过程称为重绘。由于浏览器的流布局,对渲染树的计算通常只需要遍历一次就可以完成。但table及其内部元素除外,它可能需要多次计算才能确定好其在渲染树中节点的属性,通常要花3倍于同等元素的时间。这也是为什么我们要避免使用table做布局的一个原因。

并不是所有的DOM变化都会影响几何属性,比如改变一个元素的背景色并不会影响元素的宽和高,这种情况下只会发生重绘。
因为重排和重绘的耗时是巨大的所以应当尽可能减少重排和重绘。

1、尽量不要在布局信息改变时做查询(会导致渲染队列强制刷新)
2、同一个DOM的多个属性改变可以写在一起(减少DOM访问,同时把强制渲染队列刷新的风险降为0)
3、如果要批量添加DOM,可以先让元素脱离文档流,操作完后再带入文档流,这样只会触发一次重排(fragment元素的应用)
4、 将需要多次重排的元素,position属性设为absolute或fixed,这样此元素就脱离了文档流,它的变化不会影响到其他元素。例如有动画效果的元素就最好设置为绝对定位。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多