@yacent
2018-03-22T01:31:40.000000Z
字数 4052
阅读 1000
性能优化
从你输入网站 + enter键后,发生的过程为:
重定向 => 检查DNS缓存 => DNS解析 => TCP连接 => 发请求 => 得到响应 => 浏览器处理 => 最后onload
浏览器处理 => 最后onload具体细化过程
解析HTML结构 => 加载外部脚本文件及样式表 => 解析并执行脚本代码 => 构造HTML DOM结构(ready事件执行) => 加载图片、font等外部资源 => 页面加载完毕 (onload事件执行)
总体流程
浏览器解析
1、浏览器通过请求的 URL 进行域名解析,向服务器发起请求,接收文件(HTML、CSS、JS、Images等等)。
2、HTML 文件加载后,开始构建 DOM Tree
3、CSS 样式文件加载后,开始解析和构建 CSS Rule Tree
4、Javascript 脚本文件加载后, 通过 DOM API 和 CSSOM API 来操作 DOM Tree 和 CSS Rule Tree
浏览器渲染的过程
1、浏览器引擎通过 DOM Tree 和 CSS Rule Tree 构建 Rendering Tree
2、Rendering Tree 并不与 DOM Tree 对应,比如像 标签内容或带有 display: none; 的元素节点并不包括在 Rendering Tree 中 。
3、通过 CSS Rule Tree 匹配 DOM Tree进行定位坐标和大小,是否换行,以及 position、overflow、z-index 等等属性,这个过程称为 Flow 或 Layout 。
4、最终通过调用Native GUI 的 API 绘制网页画面的过程称为 Paint 。
构建render tree前,进行了css样式的计算,然后构建render tree, then计算其 frame的位置的大小,然后同上
在构建render tree之前,先进行了css样式的计算,主要包括了一些背景,其余样式,但是宽度、高度还有位置信息还没写进去,在构建完render tree之后,需要对呈现器的高度、宽度和位置进行计算,然后进行flow/layout,即布局,布局完之后,再调用原生native GUI的PAI绘制
但是,布局过程中,会产生一些问题,因为reflow、repaint是非常耗性能的,如何控制 粗细粒度
下面,来讲一下 布局 方面的东西
Dirty位系统
为避免对所有细小更改都进行整体布局,浏览器采用了一种“dirty 位”系统。如果某个呈现器发生了更改,或者将自身及其子代标注为“dirty”,则需要进行布局。
有两种标记:“dirty”和“children are dirty”。“children are dirty”表示尽管呈现器自身没有变化,但它至少有一个子代需要布局。
全局布局和增量布局
全局布局是指触发了整个呈现树范围的布局,触发原因可能包括:
1. 影响所有呈现器的全局样式更改,例如字体大小更改。
2. 屏幕大小调整。
增量布局
布局可以采用增量方式,也就是只对dirty呈现器进行布局(这样可能存在需要进行额外布局的弊端)。
当呈现器为dirty时,会异步触发增量布局。例如,当来自网络的额外内容添加到 DOM 树之后,新的呈现器附加到了呈现树中。
异步布局和同步布局
增量布局是异步执行的。Firefox将增量布局的“reflow命令”加入队列,而调度程序会触发这些命令的批量执行。WebKit也有用于执行增量布局的计时器:对呈现树进行遍历,并对 dirty呈现器进行布局。【请求样式信息(例如“offsetHeight”)的脚本可同步触发增量布局】。【全局布局往往是同步触发的】。有时,当初始布局完成之后,如果一些属性(如滚动位置)发生变化,布局就会作为回调而触发。
换行
如果呈现器在布局过程中需要换行,会立即停止布局,并告知其父代需要换行。父代会创建额外的呈现器,并对其调用布局
Repaint & Reflow
How to triggers a reflow or a repaint
--
但是,浏览器足够聪明,其会将需要改变的DOM放进一个队列当中,然后将几次合并一次进行reflow.我们称之为 增量异步reflow
以下的DOM操作或者属性出现在js脚本当中,浏览器会马上reflow
1. offsetTop, offsetLeft, offsetWidth, offsetHeight
2. scrollTop/Left/Width/Height
3. clientTop/Left/Width/Height
4. getComputedStyle(), or currentStyle in IE
至于为何要立马进行reflow,对于在一段js代码当中,首先是无动画的切换,后再立马添加动画改变颜色,因为会cache,即浏览器一次做完再一次reflow完,所以怎样都只会看到最后的情况。但是,不适用setTimeout的话,会导致不能看到颜色的变化,因为浏览器会 将多个reflow合并之后再进行一次reflow,所以会发现这些动画被一次性搞定了。所以可以使用上面的4个方法,在动画与非动画之间,添加offsetTop让浏览器立马进行reflow
如何减少 repaint 和 reflow
浏览器高层结构
HTML 解析语法
HTML DTD(document type definition)
解析算法
原因:
1. 语言的宽容本质。
2. 浏览器历来对一些常见的无效 HTML 用法采取包容态度。
3. 解析过程需要不断地反复。源内容在解析过程中通常不会改变,但是在 HTML 中,脚本标记如果包含document.write,就会添加额外的标记,这样解析过程实际上就更改了输入内容。
所以一般浏览器会创建自定义解析器来解析HTML,该算法分为两个阶段 标记化 和 树构建
标记化
分为 数据状态、标记打开状态、标记名称状态、数据状态
树构建
CSS解析
CSS是上下文无关的语法,可以使用上下文无关语法解析器进行处理,即使用 Flex和Bison来进行词法和语法的解析
CSS Rule tree
浏览器处理脚本和样式表的顺序
脚本
网络的模型是同步的。网页作者希望解析器遇到 <script> 标记时立即解析并执行脚本。文档的解析将停止,直到脚本执行完毕。如果脚本是外部的,那么解析过程会停止,直到从网络同步抓取资源完成后再继续。此模型已经使用了多年,也在 HTML4 和 HTML5 规范中进行了指定。作者也可以将脚本标注为“defer”,这样它就不会停止文档解析,而是等到解析结束才执行。HTML5 增加了一个选项,可将脚本标记为异步,以便由其他线程解析和执行。
预解析
WebKit 和 Firefox 都进行了这项优化。在执行脚本时,其他线程会解析文档的其余部分,找出并加载需要通过网络加载的其他资源。通过这种方式,资源可以在并行连接上加载,从而提高总体速度。请注意,预解析器不会修改 DOM 树,而是将这项工作交由主解析器处理;预解析器只会解析外部资源(例如外部脚本、样式表和图片)的引用。
样式表
另一方面,样式表有着不同的模型。理论上来说,应用样式表不会更改 DOM 树,因此似乎没有必要等待样式表并停止文档解析。但这涉及到一个问题,就是脚本在文档解析阶段会请求样式信息。如果当时还没有加载和解析样式,脚本就会获得错误的回复,这样显然会产生很多问题。这看上去是一个非典型案例,但事实上非常普遍。Firefox 在样式表加载和解析的过程中,会禁止所有脚本。而对于 WebKit 而言,仅当脚本尝试访问的样式属性可能受尚未加载的样式表影响时,它才会禁止该脚本。