原文:http://www.stevesouders.com/blog/2009/04/27/loading-scripts-without-blocking/ 作者: Steve Souders 译言地址: http://www.yeeyan.com/articles/view/dexbol/40162
越来越多的网站演化成了web2.0应用程序,javascript文件也随之增加,但javascript对网站的性能有负面影响。 在主流浏览器(ie6,7)里javascript会从两个方面阻碍页面呈现:
- script标签下面的网页资源在script加载完之前会停止请求、下载。
- script标签下面的html元素在script加载完之前会停止渲染。
这个demo说明了这两种情况。demo页面里包含两个外部javascript文件 接下来是图片、css、和iframe,下面的http 瀑布图表是在ie7下首次访问demo页的情况。从图上可以看到:第一个script标签阻碍了它下面所有资源的下载,接着第二个script标签又 阻碍了所有下载,最后img css 和iframe 并行下载。 同时注意页面渲染,你会发现script标签上面的段落文本瞬间就呈现了,然而其他的html元素在两个script加载完之后才一并显示 出来。

在ie6/7 firefox2/3 Safari3 Chrome1 和 opera下 script标签会阻碍下载
浏览器是单线程的,因此在script执行的时候不下载其他资源是可以理解的,但没有理由让浏览器在script下载的时候阻碍其他资源的下载,还好最新的浏览器(ie8,safari 4,chrome 2)已经 意识到这点了。下面的图标是在ie8下首次访问刚才的demo页时的http瀑布图, 从图中可以看到script的确是并行下载,而且css文件也与script一起并行下载。但是图片和iframe依然被阻碍下载。

虽然在ie8,safari4,chrome2下script可以并发,但依然阻碍了其他资源的下载
幸运的是,有一些方法可以使script标签不会堵塞任何其他资源的下载,即使在老一些的浏览器里。不幸的是开发人员要做更多的工作。
这里有6种方法可以使script与其他资源并行下载:
- XHR eval — 通过XHR(XMLHttpRequest 对象)下载script,然后用eval方法执行XHR的responseText
- XHR Injection — 通过XHR下载script,然后建立一个script标签并把它插入文档中(body或者head标签内),接着把script标签的text属性设置为XHR的responseText的值
- XHR in Iframe — 把script标签放到一个iframe里,通过iframe下载它
- Script DOM Element — 创建script标签并把它的src属性指向你的脚本地址
- Script Defer — 添加script标签的defer属性,这个只在ie中有效,但firefox3.1也支持这个属性了
- 使用
document.write方法在页面中写入<script src="">,这个只在ie里有效
你可以通过Cuzillion查看各个方法的使用例子。下面的表格是对各个方法的总结,通过表格我们可以看到各个方法之间有一些很重要的差异。 虽然Script Defer和document.write Script标签比较混乱(mixed),但大部分方法都实现了并行下载。一些方法不能跨浏览器工作,还有一些方法需要更改你现存的代码才能正常工作。还有一个不被大家广泛讨论的区别 就是是否会引发浏览器的示忙器[busy indicator](状态栏,任务栏,标签图标,鼠标指针).如果你下载的多个script相互依赖,那么还需要这个方法可以保持多个脚本的下载执行顺序。
| Technique 方法 | Parallel Downloads 是否并行下载 | Domains can Differ 可否跨域 | Existing Scripts 是否需要更改现有脚本 | Busy Indicators是否出现示忙器 | Ensures Order 是否确保顺序 | Size (bytes) |
|---|---|---|---|---|---|---|
| XHR Eval | IE, FF, Saf, Chr, Op | no | no | Saf, Chr | - | ~500 |
| XHR Injection | IE, FF, Saf, Chr, Op | no | yes | Saf, Chr | - | ~500 |
| Script in Iframe | IE, FF, Saf, Chr, Op | no | no | IE, FF, Saf, Chr | - | ~50 |
| Script DOM Element | IE, FF, Saf, Chr, Op | yes | yes | FF, Saf, Chr | FF, Op | ~200 |
| Script Defer | IE, Saf4, Chr2, FF3.1 | yes | yes | IE, FF, Saf, Chr, Op | IE, FF, Saf, Chr, Op | ~50 |
| document.write Script Tag | IE, Saf4, Chr2, Op | yes | yes | IE, FF, Saf, Chr, Op | IE, FF, Saf, Chr, Op | ~100 |
现在的问题是哪种方法才是最好的?其实这个需要具体情况具体分析。下面的树形结构图是可以引导你找到最合适的方法,他并没有看起来的复杂。只有三个关键变量来决定结果:是否需要跨域,是否需要确保执行顺序,是否需要触发示忙器。
这下好了,如果把这个选择逻辑包装在流行的HTML 模板语言(PHP,Python,Perl等等)里,你只需要调用一个函数就能确保使用了最佳方法。
在多数情况下,Script DOM Element是最好的选择。它可以在所有浏览器下工作,不存在跨域问题,而且容易实现,但有一点要记住:他不能在所有浏览器下都确保执行顺序。如果你有多个相互依赖的script,那么你需要合并它们,或者使用其方法。 如果你有一些内联脚本需要在外部脚本执行后才能执行,那就需要同步(synchronize)他们了。我把它称作”coupling”,Coupling Asynchronous Scripts 这篇文章介绍了一些目前可以实现“coupling”的方法。
