代码改变世界

挣脱浏览器的束缚(2) - 别让脚本引入坏了事

2007-01-20 01:25 Jeffrey Zhao 阅读(...) 评论(...) 编辑 收藏

现在哪里还找得到不引入JavaScript脚本文件的Web应用?使用脚本文件的好处多多,其中最重要的可能就是提供缓存能力了。使用脚本文件之后再加上缓存,可以大大降低数据传输量,提高页面打开的速度。不过脚本文件的引入也不是简单得不值一提,我们完全有能力来优化它。

 

小心传统的脚本引入方式带来的性能问题

现在的Web应用所需的脚本越来越多,一张页面下载几百K的脚本也不再是难以想象的事情了,这就直接导致页面需要更长的时间来加载脚本。不过传统的脚本引入方式(使用<script />)会造成什么问题?再查看这点之前,我们先写一个HttpHandler来模拟一个需要较长时间才能加载的脚本。这很简单,我们只要创建一个Http Handler来做到这一点,如下:

public class Scripts : IHttpHandler {

    public void ProcessRequest (HttpContext context) {
        context.Response.ContentType = "application/x-javascript";

        System.Threading.Thread.Sleep(1500);

        context.Response.Write("//");
    }

    public bool IsReusable {
        get {
            return false;
        }
    }
}

 

我使用Thread.Sleep函数使线程休眠1.5秒,然后输出一个注释符。这样就保证了页面加载该文件需要比较长的时间,也可以将脚本的执行时间降到最低。

然后我们就写个最简单的页面,来测试一下加载这些文件的结果:

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server" id="aaa">
    <title>Untitled Page</title>
    <script type="text/javascript" language="javascript" src="Scripts.ashx?a"></script>
    <script type="text/javascript" language="javascript" src="Scripts.ashx?b"></script>
    <script type="text/javascript" language="javascript" src="Scripts.ashx?c"></script>
    <script type="text/javascript" language="javascript" src="Scripts.ashx?d"></script>
    <script type="text/javascript" language="javascript" src="Scripts.ashx?e"></script>
</head>
<body>
    ...
</body>
</html>

 

在IE里打开页面,看看这些脚本加载的情况。请注意,您可以使用IE Dev Toolbar来禁用Cache(图5)。


图5:IE中传统方式加载脚本的情况

真可谓是相当的整齐。不过整齐的背后是较低的性能:脚本文件一个一个被加载,所有脚本文件被加载完需要用8秒多时间。

那么FireFox的表现又如何?我们使用同样的页面来测试一下(图6)。


图6:FireFox中传统方式加载脚本的情况

嘿,情况差不多。

其实出现这个状况是By Design的。从上面这个简单的例子里可能还无法看出,事实上,当浏览器遇到<script />标签时,它会开始加载脚本文件,而此时页面的其它加载行为则会全部停止,包括HTML的呈现,页面或图片的下载等等。这是因为浏览器“怀疑”这些脚本文件中的一些行为可能会再页面中输出HTML。自然,我们可以使用document.write方法这么做。而很多可以放在网站中的第三方小部件,都是靠脚本文件里的document.write方法来生成HTML的。

这就让用户不太好受了。为什么我的浏览器只能建立一个连接?为什么不能一起下载?我们的带宽不是浪费了很多吗?这些都没错。还记得前一段时间台湾地震使一些Blog无法打开或者打开很慢吗?这很可能就是在页面中使用<script />引入脚本文件时造成的问题:文件下载特别慢,甚至会超时。而且当时我的blog也遇到这个问题。解决方案很简单,把<script />去掉便是。或者,您可以将<script />元素放置在“页尾”代码中,这样,页面就会打开地比较快了——不过当然,那个文件很可能还在继续加载脚本中。

这就是提高了所谓的“感知性能(Perceived Performance)”,简单的说,就是用户“感受”到的性能。用户会发现页面已经打开了,虽然还没有完全加载完,例如Snap Preview还无法工作。

 

尝试打破传统脚本引入的瓶颈

现在的脚本越做越大了,一个200K的文件,如果以20K每秒的速度下载也要10秒。如果这十秒结束之后又来个十秒……这样的网页加载速度太可怕了。我们必须尝试着打破这个瓶颈。

很有趣的是,如果您在页面中使用document.write来写一个<script />元素的话,这些脚本就可以并行下载了。我们就用下面的代码进行尝试吧:

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server" id="aaa">
    <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>');
        document.write(
            '<script type="text/javascript" language="javascript"' + 
            ' src="Scripts.ashx?d"><' + '/script>');
        document.write(
            '<script type="text/javascript" language="javascript"' + 
            ' src="Scripts.ashx?e"><' + '/script>');
    </script>
</head>
<body>
    ...
</body>
</html>

 

这样的做法似乎有些复杂,不过应该还算直观。上面代码的目的就是在页面中“写入”<script />元素,以达到引入脚本文件的目的。还是用事实说话,先来看一下IE中打开页面的效果吧(图6):


图7:IE中使用document.write加载脚本的情况

状况好多了。可以看出总是有两个脚本文件在同时下载,虽然还是受制于浏览器对于每个Domain只有2个连接的限制,但是页面加载时间已经从8秒多锐减到不到5秒了。这实在是一个绝好的消息。那么再公布一个好消息,使用这种方式引入脚本文件的话,脚本文件的执行顺序与脚本文件出现的顺序相同。我们只要安排好脚本文件的顺序,这样就可以保证脚本执行的正确性了。

嘿嘿,不管怎么说这个方法还是非常容易使用的,不是吗?那么让我们欢呼雀跃吧,因为优化就是这么简单!

很可惜事情的发展并不如我们想象的那么单纯。我们还没有试过FireFox下的状况呢。看了FireFox加载页面的数据统计图,可能就会知道,我们离目标还有很大的距离——因为它的状况和图6的显示状况完全相同,document.write这种做法在FireFox里没有起到任何作用。

为什么IE的表现和FireFox的表现不同呢?可能这就要问一下浏览器的开发者了,我们现在要做的,可能只是根据结果来为我们的应用想出更好的解决方案。

路漫漫其修远兮。