昨天为了 了解浏览器是怎么处理(.html、.css、.js)这些文件,我看了网上的好多资料,这好多资料中,有很多是通过转载、或是转载后加之自己的理解,但是因为自己对专业的词汇理解不好,还有一些作者将不同浏览器的处理过程混着说,总之,看完了,还是有很多疑虑的地方。我先根据昨天了解到的内容总结一下,日后随着学的深了,再回过来补充。2014.11.6
关于加载顺序:当浏览器获得一个html文件时,会”自上而下“加载,并在加载过程中进行解析渲染。
即为获取资源文件的过程,不同浏览器,以及他们的不同版本在实现这一过程时,会有不同的实现效果(资源间互相阻塞),。(需要学习使用timeline来做测试,我还不太会用,学会了在上文。)
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <link rel="stylesheet" href="test.css" type="text/css" /> <script src="test.js" type="text/javascript"></script> </head> <body> <p>阻塞</p> <img src="test.jpg" /> </body> </html>加载过程中遇到外部css文件,浏览器另外发出一个请求,来获取css文件。 遇到图片资源,浏览器也会另外发出一个请求,来获取图片资源。这是异步请求,并不会影响html文档进行加载,但是当文档加载过程中遇到js文件,html文档会挂起渲染(加载解析渲染同步)的线程,不仅要等待文档中js文件加载完毕,还要等待解析执行完毕,才可以恢复html文档的渲染线程。
原因:JS有可能会修改DOM,最为经典的document.write,这意味着,在JS执行完成前,后续所有资源的下载可能是没有必要的,这是js阻塞后续资源下载的根本原因。 办法:可以将外部引用的js文件放在</body>前。
虽然css文件的加载不影响js文件的加载,但是却影响js文件的执行,即使js文件内只有一行代码,也会造成阻塞。
原因:可能会有 var width = $('#id').width(),这意味着,js代码执行前,浏览器必须保证css文件已下载和解析完成。这也是css阻塞后续js的根本原因。 办法:当js文件不需要依赖css文件时,可以将js文件放在头部css的前面。
当然除了,<link href="" />这种形式,内部<style></style>这种样式定义,在考虑阻塞时也要考虑。 以上对于代码布局对文档加载的影响是比较基础的,随着浏览器的升级,以及js技术的应用,html的发展,web性能也会逐渐提高。 列举一下,以后可以逐一扩充一下:
不要在外部调用的js文件中调用运行时间较长的函数,如果一定要用,可以使用setTimeout函数。
现代浏览器存在 prefetch 优化,浏览器会另外开启线程,提前下载js、css文件,需要注意的是,预加载js并不会改变dom结构,他将这个工作留给主加载。
预加载网页,利用空余时间来提前加载该网页的后续网页。 <link rel="prefetch" href="http://"> 为js脚本添加defer属性,使得浏览器不等js脚本加载执行完,就加载后面的图片。既然图片资源都已经加载出来了,就不要在js内写document.write啦。 <script defer="true" src="JavaScript.js" type="text/javascript"/>解析的概念有些多,需要另写一篇文章。于是我就先简单的写一下。
html文档解析生成解析树即dom树,是由dom元素及属性节点组成,树的根是document对象。
DOM:文档对象模型的缩写,是html文档的对象表示,作为html的外部接口供js调用。
document.getElementById('test').style.display="none";//通过dom接口将id为test的display值设为none。
css解析将css文件解析为样式表对象。该对象包含css规则,该规则包含选择器和声明对象。
css解析.png js解析因为文件在加载的同时也进行解析,详看js加载部分。即为构建渲染树的过程,他是原来DOM树的可视化表示,构建这棵树是为了以正确的顺序绘制文档内容。 渲染树和DOM树的关系,不可见的dom元素(<head>…</head> display=none)不会被插入渲染树中。还有像一些节点的位置为绝对或浮动定位(需要css知识理解),这些节点会在文本流之外,因此会在两棵树上的不同位置,渲染树标识出真实的位置,并用一个占位结构标识出他们原来的位置。
计算样式的一些困难:
样式数据是非常大的结构,保存这样是的数据是很耗内存的。选择器迭代太深,造成太多的无用遍历。样式规则涉及非常复杂的级联,定义了规则的层次(理解:<head>里引用的外部样式表,会被局部样式表中同一属性的设置取代。还有例如body内对font的设置本来会应用于孩子元素,但是如果body的孩子元素定义font属性,则会被后者取代)。解决办法:共享样式数据。(元素可以共享样式数据的条件就是他们的状态是”一致“的。)
计算样式并生成渲染对象的过程为attachment,每个dom节点有一个attach方法,attachment的过程是同步的,调用新节点的attach方法插入到dom树中。 parser:解析, Render Tree:渲染树 Layout:安排布局
webkit主流程.png渲染过程中,webkit使用一个标志位标志所有顶层样式都已经被加载完毕,如果dom元素进行attach时,css元素并没有被加载完毕,则放置占位符,并在文档中标记,当样式表加载完毕,则重新进行计算。 说明,文档的渲染还是要等待顶层css加载完毕。接下来的gecko应该也是需要等待顶层css加载完毕,否则“css规则树”(见下文)无法建立啊
webkit渲染是一个元素与样式规则匹配的过程,Gecko则需要构建样式计算规则书,然后与dom树对应生成样式上下文数(及渲染树)。例子:
<html> <body> <div class=”err” id=”div1″> <p> this is a <span class=”big”> big error </span> this is also a <span class=”big”> very big error</span> </p> </div> <div class=”err” id=”div2″> another error </div> </body> </html> //规则 1. div {margin:5px;color:black} 2. .err {color:red} 3. .big {margin-top:3px} 4. div span {margin-bottom:4px} 5. #div1 {color:blue} 6. #div2 {color:green} 样式规则树.png解释一下:A:任意一个父级元素。B、E:代表元素选择器,B指div,E指div下的span。C、G:代表类选择器。D、F:代表:id选择器。后面的123456,代表匹配的规则。
样式上下文树.png解释一下:当遇到一个dom节点,例如:第二个div,根据css解析结果,进行规则匹配发现符合126这条规则线,我们发现,当遇到第一个div时已经匹配过12这条规则线,所以只需为规则6新增一个节点至样式上下文树的div:F节点。样式上下文树,是元素匹配样式的最终结果(原本是比例的也要换算成具体的px)。 Gecko利用样式规则树,有效的实现了样式共享。Webkit没有规则树,则需要对css解析结果进行多次遍历。出现多次的属性将会被按照正确的级联顺序进行处理最后一个生效。
根据对计算样式困难的理解,我们在编写css样式表时应该注意一下:
dom深度尽量浅。减少inline javascript、css的数量。使用现代合法的css属性。不要为id选择器指定类名或是标签,因为id可以唯一确定一个元素。不要给类选择器指定标签,类,代表具有一类属性的标签,不仅是一个,虽然可以实现,但是降低了效率。避免后代选择符,尽量使用子选择符。原因:子元素匹配符的概率要大于后代元素匹配符。后代选择符;#tp p{} 子选择符:#tp>p{}避免使用通配符,举一个例子,.mod .hd *{font-size:14px;} 根据匹配顺序,将首先匹配通配符,也就是说先匹配出通配符,然后匹配.hd(就是要对dom树上的所有节点进行遍历他的父级元素),然后匹配.mod,这样的性能耗费可想而知.参考文章: 浏览器加载和渲染html顺序 JS 和 CSS 的位置对其他资源加载顺序的影响
