各浏览器对页面外部资源加载的策略

这个总结来源于一次优化的请求,最初某个页面的加载十分缓慢,load事件迟迟无法触发,因此希望可以通过对静态文件分域名等方式对页面的外部资源进行优化,拿得load事件尽可能早地触发。

于是我查看了页面的源码,并对外部资源进行了整理,基于下面2个理念画出了一个推测的瀑布图:

  • 浏览器对同一个域只能并发2个HTTP请求 – 网上盛传已久。
  • javascript文件的加载会阻塞浏览器其他资源的加载 – 同样网上盛传已久。

然而,当我看到各浏览器中实际的瀑布图时,我知道自己又犯了一个简单的错误:太过相信所谓的权威和大众的声音,而没有更早地进行实践来检验理论的正确性……

本篇文章就使用几种流行的浏览器,针对同一个页面的外部资源加载过程进行分析,推测各浏览器加载外部资源的策略、特征,并最后给予一定的比较和总结。

测试样例

测试的页面结构如下:

  • head
    • 1.css + 1.js
  • body
    • 1.jpg + 2.jpg + 2.js + 2.css + 3.jpg + 4.jpg + 3.css + 3.js + 5.jpg + 6.jpg

共12个外部资源,加上页面本身,一次完整的加载一共有13次HTTP GET请求。

针对每一个外部资源,服务器首先会休眠5秒的时间,随后再返回相应的内容,以方便查看整个外部资源的加载过程。

测试的浏览器如下:

  • IE6
  • IE8
  • Firefox3.6
  • Firefox4.0 beta12
  • Chrome 8
  • Opera 11

IE6

IE6的瀑布图非常传统,其特征有:

  • 各资源按照在HTML中出现的顺序进行加载。
  • javascript文件会阻塞其后所有资源的加载。
  • 最大并发HTTP连接数为2个。

可见网上盛传的2个“误区”都来自IE6统治浏览器市场的时代,针对IE6的优化太多太多,大家也就习惯性地将这些结论作为公理来使用了。

IE8

和IE6完全不同的瀑布图,其特点有:

  • 最大并发HTTP连接数为6个。
  • javascript文件已经不会阻塞其他资源的加载,甚至多个javascript文件可以一起加载,并且会保证执行的顺序。
  • 会分析HTML结构,优先下载script和link标签定义的外部资源。

Firefox3.6

和IE8的几乎完全一样:

  • 最大并发HTTP连接数为6个(可在about:config中修改)。
  • javascript文件不会阻塞其他资源的加载,多个javascript文件可以一起加载。
  • 会分析HTML结构,优先下载script和link标签定义的外部资源。

Firefox4 beta12

不知是因为设计理念上的不同,还是因为beta版未照顾到这一块,Firefox4反而退化了,和Firefox3.6的区别主要体现在对资源类型的处理上,Firefox4不再严格地优先下载script和link标签定义的外部资源,而是按照HTML结构中出现的顺序来进行加载。

Chrome8

Chrome自带的工具不能很清楚地表示各请求的开始时间,所以使用了Fiddler的瀑布图,从图上可以看出,Chrome也是比较特立独行的一位,其特点有:

  • 最大并发HTTP连接数为6。
  • head部分的资源会单独下载,且阻塞body中的其他资源的加载。
  • 会优先加载script和link标签定义的资源。

Opera11

先报怨一下,Dragonfly不怎么好用来着……Opera的资源加载也比较有特色,而且很难看出规律,只能大致总结一下:

  • 最大并发HTTP连接数为5(网上有说原先版本是4)。经过网友的指正,Opera的最大并发HTTP连接数默认为16,可在opera:config - Performance - Max Connections Server查看和修改。
  • javascript文件的加载会阻塞其他script和link标签定义的外部资源的加载,如图中的2.js。但不会阻塞图片等其他资源的加载,如图中的3.js。
  • 会一定程度上对资源的优先级进行优化,但由于javascript文件要阻止后续部分资源的加载,又为了充分利用最大HTTP连接数,因此不能严格先加载所有的script和link标签定义的资源,导致瀑布图上各类型资源有相互穿插,难寻规律。

总结

  • 抛开IE6不论的话,除非是在线相册之类外部资源非常多的页面,不然没必要去追求静态资源的分域名优化。
  • 针对IE6进行静态资源分域名优化时,要严格注意javascript文件对后续资源的阻塞,进行精确计算和设计后保证资源最完美地分域名存储,以提供最大并行度。
  • 鉴于Chrome对head部分的资源会独立加载,当head部分用不满6个HTTP并发数时,是否可以将资源移到body中呢?在body中的资源又会引起其他的问题,需要谨慎考虑。
  • Opera的行为比较怪异,似乎主动设计了一个很麻烦的算法,不过考虑到其占有率,就先放在一边吧……而且号称最快的浏览器的Opera,在加载javascript文件时竟然如此笨拙……
  • Firefox4 beta12的行为让人无法理解,看来要追踪RC版是否还存在这个问题,如果存在的话可以考虑找Mozilla报个问题了。

对各浏览器加载外部资源的策略的掌握,是WPO的基本元素,虽然一直想当一个WPO的专家,却在这方面迟迟不愿实践,实在有愧于自己的理想……

最后,如果有哪位朋友了解Opera对资源加载的具体策略的,还请提供一下,以便有更清晰地认知,谢谢~!

此条目发表在 浏览器 分类目录,贴了 , 标签。将固定链接加入收藏夹。

各浏览器对页面外部资源加载的策略》有 25 条评论

  1. 宇义 说:

    好文章!感谢!

  2. afc163 说:

    想知道lz用的是什么工具,httpWatcher?

    • int08h 说:

      IE和Firefox下都用的HTTPWatch Basic Edition,因为Firebug 1.7给的瀑布图不是很直观,不如HTTPWatch舒服
      Chrome因为默认的Developer Tool给的瀑布图同样不直观,而超级插件Speed Tracer给的瀑布图也一样(怀疑Chrome内部报告请求发起时间就有问题),所以只好委屈用了Fiddler
      Opera有自带的Dragonfly可以使用

  3. aoao 说:

    ie6~7 在http1.0时并发是4个。。。1.1才是2个。。
    阻塞这块有挺多细节的。。

    • int08h 说:

      没有IE7环境一直是我的问题,但我实在懒得去离这个随着Vista一起半死不活的东西了……
      1.0的并发一直是4个,这也是标准中建议的值,1.1是因为有了keep-alive才被限制在2个的,也是受限于当年服务器的性能不怎么样,CPU有可能跑不动:)

  4. lenel 说:

    赞,最爱这种眼见为实的总结.

    之前也做过类似测试,但发现不同浏览器无论怎么优化,有一个问题没法解决
    就是静态script节点(defer抛开)的执行顺序是固定的.
    而页面的制作往往又凭借于这种固定顺序来解决文件之间的前后依赖.
    这样js服务器慢了,或者其中任何一个js请求慢了,都会导致domready延迟.
    而jquery又引导大家在这个事件里边做事.

    所以我觉得彻底解决问题的方法是抛弃静态script节点,至少极小化通过这种方式这样引入的资源.
    而为保证异步加载的js的执行顺序,通过采用模块模式开发+一个聪明的loader来做.
    commonjs modules提供模块化开发标准,玉伯的seajs标准下的不错的loader.
    无论对于性能而言,依赖关系处理,扩展性,还是对于独立模块快速共享而言,这个方案都是蛮不错的.
    虽然按标准改造老系统会有些麻烦,但做系统的时候不妨尝试,没什么难度.

    • int08h 说:

      即便defer的script节点,也同样会延迟DOMContentLoaded事件的触发,根据标准,defer脚本的执行是在HTML Parsing结束之后,DOMContentLoaded之前触发
      loader型的js开发会是未来的趋势,但现阶段来看做得不够“透明”,我也一直在思考有没有一种对开发者更加透明的方式来进行,ControlJS就是极端的向这方面发展(连document.write都给重写了)
      当然最好还是由标准来提出这样的一种方案,再由浏览器来进行支持,出现一个类似OSGI的体系

  5. Pingback 引用通告: 各浏览器对页面外部资源加载的策略 - IE浏览器中文网站

  6. fisherhe 说:

    静态资源文件和动态服务相比,两者对对服务器的性能是不一样,方便文件资源的统一管理和部署,CDN策略,cache策略等等。

  7. Cloudream 说:

    我觉得FF4这么处理body中的js/css是在模仿opera的渲染策略,没拿到css时也开始渲染网页内容,拿到css后再重绘,以更快的显示网页。

  8. 挪墨 说:

    文中IE8下的“会分析HTML结构,优先下载script和link标签定义的外部资源。 ”,请问这个结论有没有出处?

  9. Pingback 引用通告: 各浏览器对页面外部资源加载的策略 - 五马石 - 记录历程

  10. perfectworks 说:

    JS 脚本的阻塞在于,它在执行而不是下载时,会阻塞其他资源的下载

    如果你例子的 js 文件写一个死循环 1 秒的函数,就可以很明显的看出阻塞作用了

    你可以用 http://stevesouders.com/cuzillion/ 这个网站来做这些测试

  11. Pingback 引用通告: 各ブラウザ対ページ外部資源パイロットの戦略 | テクニカルブログ

  12. Pingback 引用通告: WordPress 图片延迟加载插件效果 | LiZn

  13. kc jones 说:

    请问怎么看httpwatch的瀑布图呢,谢谢啊

  14. kc jones 说:

    恩,谢谢。我其实是想问,当图出现的时候,如何阅读这个图

  15. 网络菜鸟 说:

    学习了,现在正在学习基础的东西。呵呵。

  16. 西丰 说:

    写的很好啊,实践出真知。
    但是我有一点不明白,ie8下,可以同时加载多个js文件?我这也使用了 楼主的插件,但是在ie8显示的确是,要等一个js加载完毕才会加载另一个,并不是并行的。
    只有在有缓存的情况下,才会出现你的那种情况。
    曾经看别人写这样的一句话
    下载在有些浏览器中是并行的,有些浏览器中是串行的,如IE8、Firefox3、Chrome2都是串行下载的
    而且我后来使用fiddler 测试,也能明显的看到,ie8是在加载完成第一个js,之后才发出请求去加载第二个js的。

    • int08h 说:

      我跑去重新看了下,IE8的行为确实有些古怪,在同样清空了缓存的前提下,在地址栏按回车和按刷新按钮产生的结果不同……
      如果在地址栏按回车,会变成类似IE6的串行下载
      如果按刷新按钮,会变成文中所述的并行下载

      由于我给所有.js在服务器端设置了2秒的延时,而且禁用了304的验证性缓存,如果拿的是客户端的缓存,是不会有2秒的延迟感的,所以可以肯定发送了GET请求,且返回的是200,整个过程中没有使用任何缓存

      因此这边的结论只能是,IE8在不同方式加载页面(地址栏回车、直接刷新)时会采用不同的策略,@西丰 同学如果有兴趣,也可以分别试试?

发表评论

电子邮件地址不会被公开。 必填项已被标记为 *

*

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>