浏览器对Script下载的非并行处理再说明
在4月26号下午的讲座中,我提到了“将Script放到HTML文件中尽量靠近尾部”的方法来提高用户感觉上的响应时间,有朋友对这个问题提出了疑问,因此在这里更详细的对该方法进行说明。
首先,浏览器对于script的下载是避免并行进行的。HTTP/1.1协议中规定浏览器和同一host之间只建立最多两个连接,也就是说允许的最大并行度为2(当然,对IE和Firefox来说,你都可以通过修改浏览器的设置来扩大这个并行度)。但对于Script的下载来说,浏览器在开始下载Script之后,是不会并行的下载其他element的。不会并行下载script这一点是一个事实,但浏览器为什么要采用这种策略,以及浏览器我们提到的“将Script放到HTML文件中尽量靠近尾部”到底能起到多大的作用,需要注意哪些事项,我希望在这篇文章中进一步的进行讨论。
为了讨论方便,我准备了一个简单的HTML文件:
<HEAD>
<Title>Test javascript download page</Title>
</HEAD>
<BODY>
<Script language="Javascript" src="jsdownload_j1.js"></Script>
<Script language="Javascript" src="jsdownload_j2.js"></Script>
This is a simple page to test javascript download.</br>
<input type="button" value="Click Me" name="testButton" onclick=OnB1Click()></input>
<img src="Sunset_small.JPG">
<img src="Blue_hills_small.JPG">
</BODY>
</HTML>
这个HTML文件include了两个javascript文件,含有两张图片。两个javascript文件内容分别如下:
jsdownload_j1.js:
{
alert("A simple test");
}
jsdownload_j2.js:
{
OnJ1B1Click();
}
从代码中看出,这个HTML文件很简单,带有两张图片和一个button,当button被click的时候,调用jsdownload_j2.js的OnB1Click()函数,而OnB1Click()函数是简单的调用jsdownload_j1.js文件中的OnJ1B1Click()函数。
我们来看看用IBM Page Detailer工具获得的IE浏览器访问该页面时的页面元素下载图:
从图中可以看到,javascript的下载block了其他元素的下载,因此第一个可视元素(图片)的下载完成是在1.33秒的时刻,也就是说,用户等待1.33秒才能看到页面上的第一幅图片。而如果我们把HTML中的javascript的位置放到稍后的位置,例如,改成这样:
<HEAD>
<Title>Test javascript download page</Title>
</HEAD>
<BODY>
<img src="Sunset_small.JPG">
<img src="Blue_hills_small.JPG">
<Script language="Javascript" src="jsdownload_j1.js"></Script>
<Script language="Javascript" src="jsdownload_j2.js"></Script>
This is a simple page to test javascript download.</br>
<input type="button" value="Click Me" name="testButton" onclick=OnB1Click()></input>
</BODY>
</HTML>
则得到的element下载图如下:
虽然总的下载时间比上一次稍长,但在0.72秒的时候,第一幅图片就已经下载完成,此时用户会看到页面的主要部分,因此对用户来说,页面变“快”了。
这是一个典型的利用浏览器下载Script时候的非并行特性优化页面性能的方法。接下来,我们讨论下为什么浏览器在下载Script的时候会采用非并行的方式。其实原因也不复杂:
在我们的HTML文件中,有两个Script,一个是jsdownload_j1.js,另一个是jsdownload_j2.js文件,如果允许浏览器并行下载这两个script,则可能发生jsdownload_j2.js文件先于jsdownload_j1.js文件下载完成的情况。这时如果页面上后续的一个javascript调用使用了jsdownload_j2.js文件中的函数,则此调用会立即发生(因为浏览器判断到jsdownload_j2.js文件已经下载完成了),而如果此时jsdownload_j2.js文件中的该函数需要依赖于jsdownload_j1.js中的另一个函数,由于此时jsdownload_j1.js文件还未下载完成,则此调用会产生一个javascript error。为了避免这样的情况,浏览器采用了严格按照HTML文件中定义的顺序下载Script,并严格按照非并行方式下载的策略。
最后一个需要讨论的问题,就是这种提升页面性能的方式有哪些需要注意的内容了。实际上,如同上面黑体字的讨论,如果不分青红皂白把Script文件全部放在HTML的最后部分,也是会出问题的。例如,在我们的例子中,Button的Click事件需要调用js文件中的某个函数,如果Script都被放到HTML文件的最后的话,button这个element会在script下载完成之前被render出来,如果此时用户点击了这个button,就一定会出现一个Script error。关于这个问题,有两个解决方法:
1,将Script放在合适的,尽量靠近尾部的地方,由开发人员保证不会出现这样的问题;
2,为所有的js文件中需要被调用的函数,在HTML中实现一个空函数(放在靠前的位置),这样当页面需要的js文件没有download完成时,用户点击按钮等操作之后出发一个空函数,避免了Script Error(当然,可能会confuse用户);而等js文件download完成后,这些function会被override,用户的操作能够得到正确的解决。