最近在改写博客的主题,其中一个比较重要的方面就是研究如何提升网页在浏览器中加载的效率。本 文主要从页面内资源加载的角度出发来探讨这个问题,网上也有很多关于这方面的争论,主要分为主张多资源文件从而并发下载的“主多派”和主张整合页面资源到 较少资源文件的“主少派”。那么什么才是真相?多资源文件真的能做到并发下载吗?
其实每个网站都应该就其各自的特点来进行优化,更多的时候并行下载资源和减少连接数开销是一种博弈的效果。现在的浏览器都使用并发连接来提升浏览器的性能,具体参见:并发连接数对浏览器加载速度的测试 。在我看来,提高网页加载性能的关键就在于合理的利用这些默认的数据合理的排布HTML中引用到的CSS和JS脚本文件以及合理控制这些资源文件的数量。
与浏览器遇到JavaScript是否阻塞有关
首先来看看我们的浏览器是怎么加载页面资源的,以我室友tomsheep的博客http://tomsheep.net 为例子:
首先,浏览器发起直接对目标html的请求,然后分析其中用到的资源并下载,浏览器有自己的规则来判断什么样的资源可以被并行下载,什么样的不可以,参考pagespeed的doc 所说的,浏览器对加载顺序有着特殊的喜好:JS的出现会延迟后续CSS的下载,因为JS会改变页面元素,浏览器会延迟整个页面的渲染直到JS被下载解释并执行,所以必须让CSS的链接在JS前面以达到尽可能的并行。
举个pagespeed 上的例子:
1
2
3
4
5
6
7
|
<
head
>
<
link
rel
=
"stylesheet"
type
=
"text/css"
href
=
"stylesheet1.css"
/>
<
script
type
=
"text/javascript"
src
=
"scriptfile1.js"
/>
<
script
type
=
"text/javascript"
src
=
"scriptfile2.js"
/>
<
link
rel
=
"stylesheet"
type
=
"text/css"
href
=
"stylesheet2.css"
/>
<
link
rel
=
"stylesheet"
type
=
"text/css"
href
=
"stylesheet3.css"
/>
</
head
>
|
如果我们的html代码是这样子的,那么浏览器下载CSS会被前面两个JS给阻塞住,导致如下图般瀑布式的加载:
而当CSS和JS的加载顺序变成如下代码所示的时候,就可以避免阻塞下载的情况发生:
1
2
3
4
5
6
7
|
<
head
>
<
link
rel
=
"stylesheet"
type
=
"text/css"
href
=
"stylesheet1.css"
/>
<
link
rel
=
"stylesheet"
type
=
"text/css"
href
=
"stylesheet2.css"
/>
<
link
rel
=
"stylesheet"
type
=
"text/css"
href
=
"stylesheet3.css"
/>
<
script
type
=
"text/javascript"
src
=
"scriptfile1.js"
/>
<
script
type
=
"text/javascript"
src
=
"scriptfile2.js"
/>
</
head
>
|
可以看到,页面资源在html中出现的顺序对浏览器是否并行下载资源有着重要的影响,并不是只要出现多个资源文件都能并行的下载。
与浏览器支持的并发连接数有关
在HTTP 1.1协议中要求浏览器访问同一host的连接数不得大于2,但事实上当前绝大多数浏览器都违背了这一要求,具体参见:并发连接数对浏览器加载速度的测试 ,实际的默认连接数的多少跟操作系统以及浏览器版本有关。但我们基本上可以认为,浏览器的并发连接数大于6(忽略过时的浏览器,比如IE6)。
浏览器在进行每一次请求资源的过程中,都需要进行DNS Lookup来将域名翻译成IP地址并且新建一个TCP连接(如果没有keepalive或者keepalive timeout了),因此连接越多由此带来的overhead越大。而且,一旦资源文件超过了浏览器支持的最大并发数量,那么必定有资源要被延迟下载。比 如加载某网页需要下载13个资源文件(包含原始的html)、全都是CSS不会产生JS延迟、每次请求耗时100ms,那么浏览器第一次连接用于请求 html,第二到第七次连接并发请求2-7号资源,第八到第十三次连接并发请求8-13号资源,可以看到,浏览器在并发连接的情况下也用了300ms。而 如果将13个文件合并成7个文件的话,用200+ms就能完成(单个文件增大后传输会稍慢,不过少了DNS Lookup以及TCP连接的overhead,整体性能会有一个飞跃)。
综上所述,并行下载和降低连接overhead需要达到一个平衡状态才是一个好的方案,片面的追求较少的连接数或较高的并行性都是不可取的。这个平 衡状态是因站点而已的,网站管理员需要根据各自网站的特点选用合适的技术来提升访问效率(当然服务器的性能也是相当重要的因素)。同时也推荐给大家一个网 站,http://site-perf.com/ ,它能够模拟浏览器用不同的连接数来测试你的网站,和Firebug插件一样对调优很有帮助。
常用的技术
CSS Sprites ,用来将不经常改动的小图片整合成一张大图片,在CSS中利用background- position、width和height来控制显示的区域。优点是能显著的减少连接数以减少overhead而且可以被复用。缺点是制作起来比较费功 夫,而且没有什么好办法解决repeat的背景图(因为大小未知)。
Data URL 和 DHTML ,通过Base64编码将二进制文件(比如图片)捆绑到HTML/CSS中。 优点是制作简便,也能减少连接数。缺点是BASE64在一定程度上会增大文件大小(即使用了GZip压缩);浏览器也要重新解码显示,会带来一定的性能问 题;最重要的是,无法被缓存,每次请求HTML/CSS都会加载一遍。
CDN ,全称Content Delivery Network,即内容分发网络。现在有一定规模以及并发访问量需求的站点(比如网易和新浪等)都将各自的页面资源(CSS/JS/图片等)分发在不同的 host主机上,能让浏览器同时从多个host上下载资源而且也能根据负载和网络状况等因素将用户的请求递交到离用户最近的主机上,提升可靠性。成本巨 大,不是一般人能玩得起的。当然有一些cdn站点提供诸如jquery之类的服务,在jQuery官方下载 可以看到介绍,经我试验下来微软的ajax.aspnetcdn.com响应速度最快,优点有很多,速度和稳定性咱就不提了,更重要的是对浏览者来说他们可能已经请求过该脚本并放在缓存中了。
研究了各自的利弊之后得出我博客主题所使用的策略:主要使用CSS Sprites,用Data URL来解决背景repeat问题。当然,这也是因站点而已的,对于小站点(比如我的博客)之类的,可以把所有用到的图片整合到一张图片中;对于那些大站 点,就应该把相近的功能整合到一张图中,这样就算有调整,客户端也不用下载整张大图,只需要更新修改的部分就可以。不管是CSS Sprites还是Data URL都是针对网站本身的样式来说,不适合把内容中的图片(比如新闻中的图片)捆绑进HTML/CSS/图片中。