HTML解析器输出的树是由DOM元素和属性节点组成的,它是HTML文档的对象化描述,也是HTML元素与外界(如Javascript)的接口。DOM与标签有着几乎一一对应的关系。 javascript
DOM树的构建过程为:将字节转换成字符,肯定tokens,将tokens转换成节点,而后构建 DOM 树。css
浏览器构建这个简单页面的 DOM 过程当中,在文档的 head 中遇到了一个 link 标记,该标记引用一个外部 CSS 样式表:style.css。因为预见到须要利用该资源来渲染页面,它会当即发出对该资源的请求。并进行CSSDOM树的构建。html
CSSOM 树和 DOM 树合并成渲染树,而后用于计算每一个可见元素的布局,并输出给绘制流程,将像素渲染到屏幕上。java
绘制过程以下:node
>浏览器
那么当资源中还有js时会发生什么?答案是阻塞DOM树的构建! 异步
脚本在文档的何处插入,就在何处执行。当 HTML 解析器遇到一个 script 标记时,它会暂停构建 DOM,将控制权移交给 JavaScript 引擎;等 JavaScript 引擎运行完毕,浏览器会从中断的地方恢复 DOM 构建。而所以就会推迟页面首绘的时间。async
提到首绘这里来提两个概念: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>
以上两种方式均可以实现js的异步加载。那么二者之间有什么区别呢?
因为async脚本执行顺序的不肯定性所以不能用来加载具备明显依赖关系的js会形成乱序。
DOM树表示页面结构,渲染树表示DOM节点如何显示。DOM树中的每个须要显示的节点在渲染树种至少存在一个对应的节点(隐藏的DOM元素disply值为none 在渲染树中没有对应的节点)。
渲染树中的节点被称为“帧”或“盒”,符合CSS模型的定义,理解页面元素为一个具备填充,边距,边框和位置的盒子。一旦DOM和渲染树构建完成,浏览器就开始显示(绘制)页面元素。
那么什么是重排和重绘呢
当DOM的变化影响了元素的几何属性(宽或高),浏览器须要从新计算元素的几何属性,一样其余元素的几何属性和位置也会所以受到影响。
浏览器会使渲染树中受到影响的部分失效,并从新构造渲染树。这个过程称为重排。完成重排后,浏览器会从新绘制受影响的部分到屏幕,该过程称为重绘。
因为浏览器的流布局,对渲染树的计算一般只须要遍历一次就能够完成。但table及其内部元素除外,它可能须要屡次计算才能肯定好其在渲染树中节点的属性,
一般要花3倍于同等元素的时间。这也是为何咱们要避免使用table作布局的一个缘由。
并非全部的DOM变化都会影响几何属性,好比改变一个元素的背景色并不会影响元素的宽和高,这种状况下只会发生重绘。
由于重排和重绘的耗时是巨大的因此应当尽量减小重排和重绘。
一、尽可能不要在布局信息改变时作查询(会致使渲染队列强制刷新) 二、同一个DOM的多个属性改变能够写在一块儿(减小DOM访问,同时把强制渲染队列刷新的风险降为0) 三、若是要批量添加DOM,能够先让元素脱离文档流,操做完后再带入文档流,这样只会触发一次重排(fragment元素的应用) 四、 将须要屡次重排的元素,position属性设为absolute或fixed,这样此元素就脱离了文档流,它的变化不会影响到其余元素。例若有动画效果的元素就最好设置为绝对定位。