前端性能优化 —— 文档在浏览器中的加载和渲染
文档载入顺序如下:
地址栏输入url,浏览器下载html文档后开始解析:(参考链接:http://www.cnblogs.com/Peng2014/p/4687218.html)
1:首先形成dom tree 和 render tree。
1:在<head>里面发现了link,则浏览器会加载css样式文件,这个过程是异步的,即发起请求后,不会影响下面文件的请求加载和渲染。比如若存在多个link,则这几个link会同时发起请求,这是多线程并行的。(当然多个css文件可以合并为一个文件来减少http请求个数,缩短相应时间)
2: 接着解析器开始解析body部分,此时css样式已经准备就绪,开始渲染节点。(所以css文件应该放在body的前面来加载,这样可以减少dom节点渲染成本)
3:浏览器在代码中发现一个 <img> 标签引用了一张图片,向服务器发出请求。此时浏览器不会等到图片下载完,而是继续渲染后面的代码;和css加载一样 (图片文件合并,减少HTTP请求)
4. 服务器返回图片文件,由于图片占用了一定面积,影响了后面段落的排布,因此浏览器需要回过头来重新渲染这部分代码; (最好图片都设置尺寸,避免重新渲染)
6. 浏览器发现了一个包含一行 JavaScript 代码的 <script> 标签,会立即运行该js代码; 此时阻止了渲染进程 (script最好放置页面最下面,让页面样式先呈现出来)
7. js脚本执行了语句,它令浏览器隐藏掉代码中的某个 <div>,突然就少了一个元素,浏览器不得不重新渲染这部分代码; (页面初始化样式不要使用js控制)
8. 遇到<script src="" >后加载js文件,此时阻断下面的渲染进程,等js文件加载并执行完后再继续下面的渲染流程。<所以script的请求要放在body后面>
9. 用户点了一下界面中的“换肤”按钮,JavaScript 让浏览器换了一下 <link> 标签的 CSS 路径;
10. 浏览器召集了在座的各位 <div><span><ul><li> 们,“大伙儿收拾收拾行李,咱得重新来过……”,浏览器向服务器请求了新的CSS文件,重新渲染页面。
文档又是怎样渲染出图像呈现在界面上呢?(原文链接:http://www.iteye.com/news/27795)
从上面这个图中,我们可以看到那么几个事:
1)浏览器会解析三个东西:
- 一个是HTML/SVG/XHTML,事实上,Webkit有三个C++的类对应这三类文档。解析这三种文件会产生一个DOM Tree。
- CSS,解析CSS会产生CSS规则树。
- Javascript,脚本,主要是通过DOM API和CSSOM API来操作DOM Tree和CSS Rule Tree.
2) 解析完成后,浏览器引擎会通过DOM Tree 和 CSS Rule Tree 来构造 Rendering Tree。注意:
- Rendering Tree 渲染树并不等同于DOM树,因为一些像Header或display:none的东西就没必要放在渲染树中了。
- CSS 的 Rule Tree主要是为了完成匹配并把CSS Rule附加上Rendering Tree上的每个Element。也就是DOM结点。也就是所谓的Frame。
- 然后,计算每个Frame(也就是每个Element)的位置,这又叫layout和reflow过程。(这两个过程会单独拿出来写)
3)最后通过调用操作系统Native GUI的API绘制。
那么CSS Rule Tree 又是如何 结合DOM Tree 形成 Rendering Tree 的呢?
CSS解析
CSS的解析大概是下面这个样子(下面主要说的是Gecko也就是Firefox的玩法),假设我们有下面的HTML文档:
- <doc>
- <title>A few quotes</title>
- <para>
- Franklin said that <quote>"A penny saved is a penny earned."</quote>
- </para>
- <para>
- FDR said <quote>"We have nothing to fear but <span>fear itself.</span>"</quote>
- </para>
- </doc>
于是DOM Tree是这个样子:
然后我们的CSS文档是这样的:
- /* rule 1 */ doc { display: block; text-indent: 1em; }
- /* rule 2 */ title { display: block; font-size: 3em; }
- /* rule 3 */ para { display: block; }
- /* rule 4 */ [class="emph"] { font-style: italic; }
于是我们的CSS Rule Tree会是这个样子:
注意,图中的第4条规则出现了两次,一次是独立的,一次是在规则3的子结点。所以,我们可以知道,建立CSS Rule Tree是需要比照着DOM Tree来的。CSS匹配DOM Tree主要是从右到左解析CSS的Selector,好多人以为这个事会比较快,其实并不一定。关键还看我们的CSS的Selector怎么写了。
注意:CSS匹配HTML元素是一个相当复杂和有性能问题的事情。所以,你就会在N多地方看到很多人都告诉你,DOM树要小,CSS尽量用id和class,千万不要过渡层叠下去,……
例如:
一个css规则:.text div span h1{} ,
若从左到右匹配:则先找到.text的元素,然后再子节点里找div,遍历对每一个div找下面的span,若某一个div下面没有发现span,则回溯到上面下一个div继续找,这是树的深度遍历。
若从右到左匹配:则先找到所有h1元素,然后对每个元素向上追溯找到满足条件的节点,这种方法在大部分情况下比第一种要好,但我们在写css样式的时候应尽量做到如下2点:
(1)尽量用id或则class来标识元素;
(2)尽量不要用父元素套子元素过度层叠,例如:.text div span h1
通过这两个树,我们可以得到一个叫Style Context Tree,也就是下面这样(把CSS Rule结点Attach到DOM Tree上):
所以,Firefox基本上来说是通过CSS 解析 生成 CSS Rule Tree,然后,通过比对DOM生成Style Context Tree,然后Firefox通过把Style Context Tree和其Render Tree(Frame Tree)关联上,就完成了。注意:Render Tree会把一些不可见的结点去除掉。而Firefox中所谓的Frame就是一个DOM结点,不要被其名字所迷惑了。
当Render Tree 构建好了之后浏览器就开始渲染了,接下来谈论影响渲染的两个因素reflow和repaint