在讨论这次的主题之前,我们现在看一下脚本优化的另一个问题,就是“优化难度”。在这里我所说的“优化难度”是指优化一张页面时的修改难度。例如在前一片文章中,使用document.write来引入脚本的话,其“优化难度”会非常的低——没有任何副作用,不用修改其它任何代码。不过它的效果似乎还不太理想,因为仅仅优化了IE下的体验,在FireFox里却没有任何作用。  很可惜,我回想了几乎所有的优化方式,再也没有找到优化难度如此低的做法了。对于其它的方式,我们都必须在页面的别处进行修改,优化效果越好,修改量越大。对于这些优化方式,我们就必须编写合适的组件,将一些逻辑封装起来。这样可以在一定程度上方便使用,降低优化难度。比较document.write与defer  那么这又何document.write或者defer有什么关系?且听我慢慢道来。  <script />的defer属性在标准里的定义是这样的:  When set, thisboolean attribute provides a hint to the user agent that the script isnot going to generate any document content (e.g., no "document.write"in javascript) and thus, the user agent can continue parsing andrendering.  我们当时遇到JS无法并行下载的原因就是浏览器认为在脚本中可能会输出HTML内容。defer属性的作用就是告诉浏览器,脚本里不会输出任何信息。果然,当我们在IE里使用defer属性时,脚本没有被阻塞,其效果和document.write一样。不过在FireFox里依旧不行,这样的实现实在让人费解。  都说FireFox标准,看来在细节上也不尽然。  那么为什么我们在之前使用了document.write而不是defer属性呢?两者效果相同,但是明显使用defer属性更加直观啊。  defer属性使用起来的确直观和方便。不过,效果真的相同吗?我们可以通过以下的例子试试看。
document.write

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
<script type="text/javascript" language="javascript">
document.write(
'<script type="text/javascript" language="javascript"' +
' src="Scripts.ashx?a"><' + '/script>');
document.write(
'<script type="text/javascript" language="javascript"' +
' src="Scripts.ashx?b"><' + '/script>');
document.write(
'<script type="text/javascript" language="javascript"' +
' src="Scripts.ashx?c"><' + '/script>');
</script>
</head>
<body>
<input type="button" value="Click" />
<script type="text/javascript" language="javascript" src="Scripts.ashx?a">
alert('Hello World');
</script>
</body>
</html>

  然后再使用<scriptdefer="defer"></script>的方式引入一下。打开两个页面进行比较就会发现,如果使用document.write的话,在脚本加载完毕之前按钮不会显示,也不会出现提示框;而如果使用defer属性的话,按钮就立即出现了,也会马上出现提示。  这可麻烦了。如果页面上的元素过早出现,用户在脚本加载完之前进行操作是否会有问题?如果页面里存在直接执行的脚本(如上例的alert调用),在脚本文件加载完之前是否能够执行?如果上面两个问题的答案有任何一个是肯定的话,那么恭喜您,使用defer属性就会造成错误了。而且这个问题的解决方案实在不太容易找到,这大大增加了“优化难度”。  而且更为关键的是,FireFox同样不支持defer属性的效果。这直接导致了defer属性全面落后于使用document.write的优化方式。既然这样,我们为什么要用它?事实上defer属性用的实在不多,这是个非常典型的的“鸡肋”特性。  那么,哪里有使用defer属性的应用呢?我想应该是有的吧,虽然我不知道。突破两个连接的限制  在上一片文章里我们可以看到,虽然document.write方法可以让脚本文件并行加载,但是它依旧受到浏览器的限制。根据HTTP协议的标准,对于同一个Domain,只能同时存在两个连接。在这点上,亲爱的浏览器们都乖乖的实现了。我们如果想要突破这种限制,就要增加域名。不过其实浏览器判断域名的方式是非常严格的,同一域名下的子域名,同一域名不同端口,都不算相同。一般来说,使用子域名来增加并行加载的连接数是比较常用的做法。  应该已经有不少朋友知道这个方法,它的应用实在太普遍了。不过请注意,请求任意资源时都会建立连接,浏览器对于某一域名的连接并不区分其作用。因此,无论下载图片,CSS文件,JavaScript文件,或者是XMLHttpRequest对象建立的AJAX连接,都属于“两个连接”之内,在优化时往往需要注意这一点。另外,一个浏览器里同时建立的连接数也不是越多越好,根据实验资料显示,浏览器可以同时建立6到7连接最为合适。因此,我们使用3到4个子域名是比较妥当的。  我们现在就来看一下使用效果。在开发时要出现这个效果,我们可以修改C:\WINDOWS\system32\drivers\etc\hosts文件来设置本地的DNS映射。如下:
在Hosts文件里添加如下映射

127.0.0.1 [url]www.test.com[/url]
127.0.0.1 sub0.test.com
127.0.0.1 sub1.test.com
127.0.0.1 sub2.test.com
127.0.0.1 sub3.test.com
127.0.0.1 sub4.test.com
127.0.0.1 sub5.test.com

  我们可以多加一些子域名,方便以后使用。  接下来我们就可以在页面里从多个不同的子域名加载脚本文件,如下:
从不同子域名加载脚本文件

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
<script type="text/javascript" language="javascript">
document.write('<script type="text/javascript" language="javascript"' +
' src="[url]http://sub0.test.com/Scripts.ashx?a[/url]"><' + '/script>');
document.write('<script type="text/javascript" language="javascript"' +
' src="[url]http://sub0.test.com/Scripts.ashx?b[/url]"><' + '/script>');
document.write('<script type="text/javascript" language="javascript"' +
' src="[url]http://sub1.test.com/Scripts.ashx?c[/url]"><' + '/script>');
document.write('<script type="text/javascript" language="javascript"' +
' src="[url]http://sub1.test.com/Scripts.ashx?d[/url]"><' + '/script>');
document.write('<script type="text/javascript" language="javascript"' +
' src="[url]http://sub2.test.com/Scripts.ashx?e[/url]"><' + '/script>');
</script>
</head>
<body>
...
</body>
</html>

  在浏览器打开页面试试看?还记得当初我们加载页面用了多少时间吗?8秒多!而现在已经能够在不到2秒内加载完毕了(如图2)。
图8:使用多个子域名进行并行加载  可惜我们还要优化FireFox浏览器里的情况,下次我们就来讨论这个问题。接下来的优化方案会有一定的难度,不过只要我们利用得当,将会大大提高Perceived Performance。