Hello World
Spiga

挣脱浏览器的束缚(3) - 两个连接还不够“并行”

2007-01-22 14:42 by 老赵, 6076 visits

在讨论这次的主题之前,我们现在看一下脚本优化的另一个问题,就是“优化难度”。在这里我所说的“优化难度”是指优化一张页面时的修改难度。例如在前一片文章中,使用document.write来引入脚本的话,其“优化难度”会非常的低——没有任何副作用,不用修改其它任何代码。不过它的效果似乎还不太理想,因为仅仅优化了IE下的体验,在FireFox里却没有任何作用。

很可惜,我回想了几乎所有的优化方式,再也没有找到优化难度如此低的做法了。对于其它的方式,我们都必须在页面的别处进行修改,优化效果越好,修改量越大。对于这些优化方式,我们就必须编写合适的组件,将一些逻辑封装起来。这样可以在一定程度上方便使用,降低优化难度。 

 

比较document.write与defer

那么这又何document.write或者defer有什么关系?且听我慢慢道来。

<script />的defer属性在标准里的定义是这样的:

When set, this boolean attribute provides a hint to the user agent that the script is not going to generate any document content (e.g., no "document.write" in javascript) and thus, the user agent can continue parsing and rendering.

我们当时遇到JS无法并行下载的原因就是浏览器认为在脚本中可能会输出HTML内容。defer属性的作用就是告诉浏览器,脚本里不会输出任何信息。果然,当我们在IE里使用defer属性时,脚本没有被阻塞,其效果和document.write一样。不过在FireFox里依旧不行,这样的实现实在让人费解。

都说FireFox标准,看来在细节上也不尽然。

那么为什么我们在之前使用了document.write而不是defer属性呢?两者效果相同,但是明显使用defer属性更加直观啊。

defer属性使用起来的确直观和方便。不过,效果真的相同吗?我们可以通过以下的例子试试看。

<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>

 

然后再使用<script defer="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映射。如下:

127.0.0.1 www.test.com
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="http://sub0.test.com/Scripts.ashx?a"><' + '/script>');
        document.write('<script type="text/javascript" language="javascript"' + 
            ' src="http://sub0.test.com/Scripts.ashx?b"><' + '/script>');
        document.write('<script type="text/javascript" language="javascript"' + 
            ' src="http://sub1.test.com/Scripts.ashx?c"><' + '/script>');
        document.write('<script type="text/javascript" language="javascript"' + 
            ' src="http://sub1.test.com/Scripts.ashx?d"><' + '/script>');
        document.write('<script type="text/javascript" language="javascript"' + 
            ' src="http://sub2.test.com/Scripts.ashx?e"><' + '/script>');
    </script>
</head>
<body>
    ...
</body>
</html>

 

在浏览器打开页面试试看?还记得当初我们加载页面用了多少时间吗?8秒多!而现在已经能够在不到2秒内加载完毕了(如图2)。


图8:使用多个子域名进行并行加载

 

可惜我们还要优化FireFox浏览器里的情况,下次我们就来讨论这个问题。接下来的优化方案会有一定的难度,不过只要我们利用得当,将会大大提高Perceived Performance。

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

27 条回复

  1. 老赵
    admin
    链接

    老赵 2007-01-22 14:51:00

    这篇讲的东西大家应该都已经比较清楚了,呵呵……

  2. Hunts.C
    *.*.*.*
    链接

    Hunts.C 2007-01-22 15:03:00

    受教

  3. 老赵
    admin
    链接

    老赵 2007-01-22 15:26:00

    @Hunts.C
    有用就好。:)

  4. vgyh[未注册用户]
    *.*.*.*
    链接

    vgyh[未注册用户] 2007-01-22 15:39:00

    您想日赚百元、千元、甚至万元吗? -----选准项目=不白打工,现在机遇来了。"OKTE超级搜索 "是国内网站100强之一,排名第8强。目前发展强劲,大有 当国内老大之趋!"OKTE超级搜索"已于元旦推出"OK宝", 这是该网会员赚钱的第二个项目;从现在到2月28日是测试 阶段,2月28日后正式运行了;会员们如果不发展下线会员 ,仅做"OK宝",每天也会有几元钱收入;因为"OK宝"是于现有的钻石制度,所以,如果您发展有下线会员,那么您每天的收入就会成倍增长(如:您现有100名下线会员, 平均每天每个会员赚了1元,那么,你每天就能赚到100元 钱;如果您有1000名下线会员,那么,您每天就能赚到1000元了,依次类推。。。。。。,而且每日付款,没有上、下限)。由于推出"OK宝",该网站更有活力,会员赚钱更快了;作为会员,机遇已经来临,因此,各位朋友,要紧紧抓住机遇,赶快注册成会员。注册就是股东,以后除了每月参加分红外,每天做"OK宝",收入更喜人。时不 我待,抢占先机,赶快注册,占领市场,机不可失!展望未来,十分美好,朋友们,努力吧! 注册网址: Http://www.oktie.cn/qq.asp?n=yulingli复制以上网址,粘贴到地址栏,按回车键。下载,安装,重新打开IE 开浏览器,随后注册。你就是OKTE的股东了。

  5. 万川[未注册用户]
    *.*.*.*
    链接

    万川[未注册用户] 2007-01-22 16:25:00

    哇赛,太经典了

  6. 老赵
    admin
    链接

    老赵 2007-01-22 16:59:00

    @万川
    :)

  7. birdshome
    *.*.*.*
    链接

    birdshome 2007-01-22 22:09:00

    在提升下载效率方面,是不是合理的利用客户端缓存似乎更简单呢?

  8. 在北京的湖南人
    *.*.*.*
    链接

    在北京的湖南人 2007-01-22 22:27:00

    真是感谢老赵!这么好的经验拿出来分享,还写得这么好!

  9. 老赵
    admin
    链接

    老赵 2007-01-22 22:34:00

    @birdshome
    您说的没错,缓存自然也是必须的,所有的静态文件都应该被缓存。可能对于脚本文件来说,只要被缓存了,就只会被下载一次,除非清空缓存。所以这样的优化往往是在项目的功能开发后期再进行的。不过也有例外,特别是需要实在大量的脚本文件的情况下,这样的优化是从设计期开始的。:)
    当然,还有加载脚本文件只是第一步,以后还会有别的优化方式,优化别的方面。

  10. 老赵
    admin
    链接

    老赵 2007-01-22 22:35:00

    @在北京的湖南人
    谢谢,我也是有了“实践”才有了体会。
    还有,其实我的文笔还需要加强,对于写Blog文章,我还是显得比较随意的。:)

  11. 在北京的湖南人
    *.*.*.*
    链接

    在北京的湖南人 2007-01-22 23:07:00

    其实在去年12月份,我才开始学习html,C,C#,但是到现在,我已经是5000月薪的asp.net开发人员,都是跟园子里的很多宗师分不开的。园子里提供的分享平台,让我这种经过三个月培训的程序员有了晋升自己的机会。

  12. 老赵
    admin
    链接

    老赵 2007-01-22 23:19:00

    @在北京的湖南人
    继续努力。:)

  13. ddee[未注册用户]
    *.*.*.*
    链接

    ddee[未注册用户] 2007-01-23 09:01:00

    @Jeffrey Zhao
    tks~~~你的努力对我们相当有帮助

  14. 老赵
    admin
    链接

    老赵 2007-01-23 09:14:00

    @ddee
    谢谢:)

  15. 在北京的湖南人
    *.*.*.*
    链接

    在北京的湖南人 2007-01-23 09:59:00

    对了,如果视频文件wma,wmv,flv在不同的二级域名下能不能也做到并发呢?
    我猜想基于你上面的分析,应该可行,还想请教!因为我现在就在做播客网站

  16. 海纳百川
    *.*.*.*
    链接

    海纳百川 2007-01-23 10:03:00

    @Jeffrey Zhao ,

    学习了,收藏了,你的努力相信对大家很有帮助!但愿你的文章篇篇精彩。

  17. 老赵
    admin
    链接

    老赵 2007-01-23 10:08:00

    @海纳百川
    谢谢,我会继续努力的。:)

  18. 老赵
    admin
    链接

    老赵 2007-01-23 10:08:00

    @在北京的湖南人
    播放媒体文件应该不受这个限制。
    浏览器的限制是HTTP标准制定的,播放媒体文件不属于浏览器的行为,也不用HTTP协议,呵呵。

  19. 海纳百川
    *.*.*.*
    链接

    海纳百川 2007-01-23 11:27:00


    @Jeffry Zhao,
    你说的“对于脚本文件来说,只要被缓存了,就只会被下载一次,除非清空缓存”,是指脚本文件下载到客户端的硬盘中了吗?如果是这样的话,在后期维护时JS脚本文件文件怎么办呢?

    上面说的增加硬解析,利用多个连接并行加载脚本文件,可以使用增加主机头的方式吗?

    谢谢

  20. 老赵
    admin
    链接

    老赵 2007-01-23 11:45:00

    @海纳百川
    修改了脚本之后,Last-Modified改变了,浏览器就会重新下载了。
    您可以使用增加主机头的做法。
    // 浏览器只认Domain,是否指向同一个IP没有任何关系。:)

  21. 海纳百川
    *.*.*.*
    链接

    海纳百川 2007-01-23 19:02:00

    To Jeffrey Zhao ,

    在我的博客(http://www.cnblogs.com/david-weihw)中转载了你的文章,并且写上了对讨论结果的总结和我的心得。若有不妥,请告诉我。谢谢!

  22. Leem
    *.*.*.*
    链接

    Leem 2007-01-23 20:37:00

    cool

  23. 老赵
    admin
    链接

    老赵 2007-01-23 20:42:00

    @Leem
    thx. :)

  24. 老赵
    admin
    链接

    老赵 2007-01-23 20:42:00

    @海纳百川
    欢迎转载,没有任何问题。:)

  25. 老赵
    admin
    链接

    老赵 2007-01-24 12:05:00

    @匡匡
    我会继续写的。:)

  26. Hand[未注册用户]
    *.*.*.*
    链接

    Hand[未注册用户] 2007-11-26 18:59:00

    太受教了,看来要多点来这里吸收知识

  27. price
    113.111.158.*
    链接

    price 2013-01-19 22:05:44

    thank you very much 受教了

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我