浏览器渲染机制

浏览器加载和渲染html的顺序

  1. IE下载的顺序是从上到下,渲染的顺序也是从上到下,下载和渲染是同时进行
  2. 在渲染到页面的某一部分时,其上面的所有部分都已经下载完成(并不是说所有相关联的元素都已经下载完)
  3. 如果遇到语义解释性的标签嵌入文件(js脚本,css样式),那么此时IE的下载过程会启用单独连接进行下载
  4. 并且在下载后进行解析,解析过程中,停止页面所有往下元素的下载,阻塞加载
  5. 样式表在下载完成后,将和以前下载的所有样式表一起进行解析,解析完成后,将对此前所有元素(含以前已经渲染的)重新进行渲染
  6. JS、CSS中如有重定义,后定义函数将覆盖前定义函数

JS的加载

  1. 浏览器对于Javascript的运行有两大特性:1)载入后马上执行,2)执行时会阻塞页面后续的内容(包括页面的渲染、其他资源的下载)。于是,如果有多个js文件被引入,那么对于浏览器来说,这些js文件被串行的载入,并依次执行。因为JavaScript可能会操作HMTL文档的DOM树,所以,浏览器一般都不会像并行下载css文件那样并行下载js文件,因此这是js文件的特殊性造成的。所以,如果你的JavaScript想操作后面的DOM元素,基本上来说,浏览器都会报错说对象找不到。因为JavaScript执行时,后面的HTML被阻塞住了,DOM树时还没有后面的DOM节点。所以程序报错了。
  2. 当所有样式表都下载完成以后页面才开始一起解析css文件开始渲染页面
  3. 因为浏览器的加载是从上到下一行一行的加载的,所以如果页面同时定义了两个相同命名的js函数,后面的函数会覆盖前面的函数

HTML页面的加载和解析流程

  1. 用户输入网址(假设是个html页面,并且第一次访问),浏览器向服务器发出请求,服务器返回html文件
  2. 浏览器开始载入html代码,发现head标签内一个link标签引用外部的css文件
  3. 浏览器又发出css文件的请求,服务器返回这个css文件
  4. 浏览器继续载入html中body部分的代码,并且css文件已经拿到手了,可以开始渲染页面了
  5. 浏览器在代码中发现一个img标签引用了一张图片,向服务器发出请求。此时浏览器不会等到图片下载完,而是继续渲染后面的代码
  6. 服务器返回图片文件,由于图片占用了一定面积,影响后面段落的排布,因此浏览器需要回过头来重新渲染这部分代码
  7. 浏览器发现一个包含一行JavaScript代码的script标签,赶快运行它
  8. javascript脚本执行这条语句,它命令浏览器隐藏代码中的某个div。杯具啊,突然就少了这么一个元素,浏览器不得不重新渲染这部分代码
  9. 终于等到html标签的到来,浏览器泪流满面
  10. 等等,还没完,用户点了一下界面中的换肤按钮,JavaScript让浏览器换了一下link标签中的css路径
  11. 浏览器召集了在座的各位<div><span><ul><li>们,“大伙儿收拾收拾行李,咱得重新来过……”,浏览器向服务器请求了新的CSS文件,重新渲染页面。

DOMContentLoaded事件

DOMContentLoaded事件在许多Webkit浏览器以及IE9上都可以使用。

DOMContentLoaded事件本身不会等待CSS文件、图片、iframe加载完成。

触发时机:加载完页面,解析完所有标签(不包括执行CSS和JS),并如规范中所说的设置interactive和执行每个静态的script标签中的JS,然后触发。而JS的执行,需要等待位于它前面的CSS加载(如果是外联的话)、执行完成,因为JS可能会依赖位于它前面的CSS计算出来的样式。

现代浏览器会并发的预加载CSS,JS,也就是一开始就并发的请求这些资源,但是,执行CSS和JS的顺序还是按原来的依赖顺序(JS的执行要等待位于其前面的CSS和JS加载、执行完)。先加载完成的资源,如果其依赖还没加载、执行完,就只能等着。img是否需要解码、绘图(paint)出来,确实需要等CSS加载、执行完才能知道。也就是说,CSS会阻塞img的展现!那么JS呢?

在有JS而没有CSS的页面中,img居然能够在收到数据后就立刻开始解码、绘图(paint),也就是说,JS并没有阻塞img的展现!这跟我们以前理解的JS会阻塞img资源的传统观念不太一样,看来Chrome对img的加载和展现做了新的优化。
执行顺序简单归纳为:==立即执行函数 =》domReady =》 onload==

js执行时机

head里的script标签会阻塞后续资源的载入以及整个页面的生成 。所以很多网站把javascript放在网页的最后面,或者动用了window.onload或是docmuemt ready之类事件。

实现等待DOM被完全加载后才调用JS文件
1) 将所有的JS文件都放在最后面后再进行调用
2) 将JS代码放在标签里面

Javascript并非完全的按顺序解释执行,而是在解释之前会对Javascript进行一次“预编译”,在预编译的过程中,会把定义式的函数优先执行,也会把所有var变量创建,默认值为undefined,以提高程序的执行效率。当JavaScript引擎解析脚本时,它会在预编译期对所有声明的变量和函数进行处理。

  1. 在执行前会进行类似“预编译”的操作:首先会创建一个当前执行环境下的活动对象,并将那些用var申明的变量设置为活动对象的属性,但是此时这些变量的赋值都是undefined,并将那些以function定义的函数也添加为活动对象的属性,而且它们的值正是函数的定义。

  2. 在解释执行阶段,遇到变量需要解析时,会首先从当前执行环境的活动对象中查找,如果没有找到而且该执行环境的拥有者有prototype属性时则会从prototype链中查找,否则将会按照作用域链查找。遇到var a = …这样的语句时会给相应的变量进行赋值(注意:变量的赋值是在解释执行阶段完成的,如果在这之前使用变量,它的值会是undefined) 所以,就会出现当JavaScript解释器执行下面脚本时不会报错

    1
    2
    3
    4
    5
    6
    7
    8
    9
    f(); // 调用函数,正确执行
    function f(){
    alert(1);
    }
    f();// 调用函数,返回语法错误
    var f = function(){
    alert(1);
    }

上面示例中定义的函数仅作为值赋值给变量f,所以在预编译期,JavaScript解释器只能够为声明变量f进行处理,而对于变量f的值,只能等到执行期时按顺序进行赋值,自然就会出现语法错误,提示找不到对象f。

JavaScript解释器在执行脚本时,是按块来执行的。通俗地说,就是浏览器在解析HTML文档流时,如果遇到一个script标签,则JavaScript解释器会等到这个代码块都加载完后,先对代码块进行预编译,然后再执行。执行完毕后,浏览器会继续解析下面的HTML文档流,同时JavaScript解释器也准备好处理下一个代码块。由于JavaScript是按块执行的,所以如果在一个JavaScript块中调用后面块中声明的变量或函数就会提示语法错误。虽然说,JavaScript是按块执行的,但是不同块都属于同一个全局作用域,也就是说,块之间的变量和函数是可以共享的。

备注:document.write()会把输出写入到脚本文档所在的位置,浏览器解析完documemt.write()所在文档内容后,继续解析document.write()输出的内容,然后在继续解析HTML文档。

1
2
3
4
5
document.write('<script type="text/javascript" src="test.js"><//script>');
document.write('<script type="text/javascript">');
document.write('alert(2);')
document.write('alert("我是" + tmpStr);');
document.write('<//script>'); //]]>

IE下,用Document.Write方法引用js文件时,js文件会出现尚未加载就直接调用的情况,因此建议将引用的JS文件单独放在一个script块中。以确保引用的js文件完全加载后,再继续执行后面的Document.Write内容。在js里出现同名函数后,你在web页面里调用改js函数后,总是调用页面中最后一个加载的函数。

jQuery DomReardy事件

在Jquery里面,我们可以看到两种写法:$(function(){}) 和$(document).ready(function(){})
这两个方法的效果都是一样的,都是在dom文档树加载完之后执行一个函数(注意,这里面的文档树加载完不代表全部文件加载完)。
而window.onload是在dom文档树加载完和所有文件加载完之后执行一个函数。也就是说$(document).ready要比window.onload先执行。

那么Jquery里面$(document).ready函数的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
document.ready = function (callback) {
  //兼容FF,Google
 if (document.addEventListener) {
  document.addEventListener('DOMContentLoaded', function () {
document.removeEventListener('DOMContentLoaded', arguments.callee, false);
callback();
}, false)
}
//兼容IE
else if (document.attachEvent) {
document.attachEvent('onreadytstatechange', function () {
if (document.readyState == "complete") {
document.detachEvent("onreadystatechange", arguments.callee);
callback();
}
})
}
else if (document.lastChild == document.body) {
callback();
}
}
window.onload = function () {
alert('onload');
};
document.ready(function () {
alert('ready');
});

转载自http://www.cnblogs.com/chenlogin/p/5221562.html