《High
Performance Web Sites》 :Chapter 1
法则 1: 尽可能减少HTTP请求数 Make Fewer HTTP Requests
在Chapter
A中介绍的性能黄金法则,揭示了一个现象:只有10~20%的用户响应时间是花在请求html源文件上,剩下的80~90%的时间则是在请求页面中的各个组件(图片,script,样式表,flash等)。因此,我们的第一个提速的方法就是尽可能的减少这些组件的数量,我们的目的就是要减少HTTP请求的数量。
减少页面组件的建议,通常会让人在性能和产品设计上产生冲突。毕竟我们的页面现在所蕴含的元素越来越多,更多的小图片,不同的样式表,script等,这一章节,我会介绍一些技术方法来帮助我们平衡性能与产品设计上的冲突。这些将要出场的技术包括:image
maps, CSS sprites,inline
images以及尽量使用独立的js和样式表文件。使用这些技术方法在我们的案例中能减少50%的响应时间。
Image
Maps
将一个页面中所要引用的图片整合成一个单一的图片文件,按顺序排好,再分切出里面的链接区域。这样对整个图片群的需求样式没有变,但减少了对图片的http请求数。
图1-1显示的是一个有五个图片组成的导航栏,每个图片对应一个独立的超链接。我们常规的做法,当然就是做五个图片,然后为每个图片做一个链接;但为了更高效,我们把五张独立的图片做成一张image
map,这样从五个HTTP请求,就变成了一个HTTP请求,相应的响应时间也就会变的更快了。

您可以试一下下面的两个链接,自己体会一下Image
maps所带来的速度上的不同。
No Image Map
http://stevesouders.com/hpws/imagemap-no.phpImage
Map
http://stevesouders.com/hpws/imagemap.php 如果使用IE6在DSL(~900Kbps)的网络环境下,用image
map的方法组成的导航栏要比用单独图片文件的所组成的导航栏要快56%(354ms 比 799ms).这是因为image
map会减少四个HTTP请求。
Image
Map最常用的实现方式是使用HTML的map标签,把大图片分成一个一个的小块,并设置其不同的链接。如下:
<img usemap="#map1"
border=0 src="http://7career.org/images/imagemap.gif">
<map
name="map1">
<area shape="rect" coords="0,0,31,31"
href="http://7career.org/home.html" title="Home">
<area
shape="rect" coords="36,0,66,31" href="http://7career.org/gifts.html"
title="Gifts">
<area shape="rect" coords="71,0,101,31"
href="http://7career.org/cart.html" title="Cart">
<area
shape="rect" coords="106,0,136,31" href="http://7career.org/settings.html"
title="Settings">
<area shape="rect" coords="141,0,171,31"
href="http://7career.org/help.html"
title="Help">
</map>
但是它所带来的缺点就是你得手动确定图片的坐标,这会比较乏味和容易出错,并且它只适合把图片都组合在一个长方形的区域里。
CSS
Sprites (您可以参考YouTube和iGoogle的首页,它们就是采用的这种优化方式)
与image maps类似,CSS
Sprites也是把若干小图片合成一个大图片,但是CSS Sprites方式更灵活。为了实现CSS
Sprites,是把各个小图片像组成一个棋盘一样地合成一个图片。如下图:

然后通过HTML中任何能支持背景图片的元素,如<span>或<div>,再通过CSS中的background-position属性来定位要显示的大图片中的某个小图片的位置。如下,就是要在上面给出的在图片中使用"My"这个图标来充当下面这个div的背景:
<div
style="background-image:
url('a_lot_of_sprites.gif');
background-position: -260px
-90px;
width: 26px; height:
24px;">
</div>
我把前面我介绍image map的例子转成CSS
Sprites的形式:把导航栏的五个链接都放到一个名为navbar的DIV中。每个链接都有一个SPAN元素,在#navbar的样式中为SPAN元素定义了背景图片spritebg.gif,但每个SPAN都有一个不同的class以指明其具体显示的背景图片的偏移位置,正是利用了CSS中的background-position属性。
<style>
#navbar span
{
width:31px;
height:31px;
display:inline;
float:left;
background-image:url(/images/spritebg.gif);
}
.home {
background-position:0 0; margin-right:4px; margin-left: 4px;}
.gifts {
background-position:-32px 0; margin-right:4px;}
.cart {
background-position:-64px 0; margin-right:4px;}
.settings {
background-position:-96px 0; margin-right:4px;}
.help {
background-position:-128px 0;
margin-right:0px;}
</style>
<div id="navbar"
style="background-color: #F4F5EB; border: 2px ridge #333;
width:180px; height: 32px; padding: 4px 0
4px 0;">
<a href="javascript:alert('Home')"><span
class="home"></span></a>
<a
href="javascript:alert('Gifts')"><span
class="gifts"></span></a>
<a
href="javascript:alert('Cart')"><span
class="cart"></span></a>
<a
href="javascript:alert('Settings')"><span
class="settings"></span></a>
<a
href="javascript:alert('Help')"><span
class="help"></span></a>
</div>
这比image
map的方式的例子要更快:342ms VS
354ms,但是他们之间的实现方式只有很小的不同。但重要的是,这可比用单独的五个图片的例子要快57%了。
CSS Sprites
http://stevesouders.com/hpws/sprites.php 我们看到,image
map的方式要求所有的图片是连续的组合在一起的,而CSS Sprites没有这个限制。关于CSS Sprites的优缺点在Dave Shea的权威文章
"CSS Sprites: Image Slicing's
Kiss of Death"中已经有详细的介绍,但我已经从CSS Sprites中体会到它的优点:减少了HTTP请求,比image
maps灵活。另一个让我没想到的优点是它减少了下载的数据量。大多数人可能会认为一个拼合成的大图片肯定要比这此小图片的总量要大,因为它会有一些小图片的间隔区域。实际上正相反,大图片减少了图片中的color
tables和格式信息等,而使得大图片比一堆小图片实际size要小一些。
如果你的网站中有很多背景图片,按钮图片,导航栏图片,那么你应该用CSS
Sprites方式来优化你的页面了。(您可以参考YouTube和iGoogle的首页,它们就是采用的这种优化方式)
Inline
images(注:IE暂不支持,您可以跳过这一部分;但说不定什么时候IE就支持了)
我们上面所做的都是为了减少HTTP请求,现在有一个更绝的方式,把所有的图片都以base64编码以源代码的形式写在HTML源码里:
data:[<mediatype>][;base64],<data>
所有对图片的HTTP请求,都化在了对HTML源文件的第一次请求里。
我们在HTML中肯定都用过ftp:,file:,mailto:这样的标签,实际上像这样的标签还有很多,只是我们平常不太使用,像:smtp:,pop:,dns:,whois:,finger:,daytime:,news:,urn:等等.
data:URL标签是在1995年第一次提出,按
RFC2397规范的描述:它是"allows inclusion
of small data items as 'immediate'
data.(允许在页面中包含一些小的即时数据)"。如一个内嵌的小红星的图片可以这样引用:(在firefox下可以出来)
<IMG ALT="Red
Star"
SRC="data:image/gif;base64,R0lGODlhDAAMALMLAPN8ffBiYvWW
lvrKy/FvcPewsO9VVfajo+w6O/zl5estLv/8/AAAAAAAAAAAAAAAACH5BAEA
AAsALAAAAAAMAAwAAAQzcElZyryTEHyTUgknHd9xGV+qKsYirKkwDYiKDBia
tt2H1KBLQRFIJAIKywRgmhwAIlEEADs=">
这样是挺方便吧,但它的不足之处也很明显:目前IE不支持;再就是FireFox1.5所能处理的line
image有大小限制,不能超过100K;base64的编码会增加HTML的容量,总体下载量会增多。
Inline Images:
http://stevesouders.com/hpws/inline-images.php
data:URL是内嵌在页面中的,所以它不会在页面之间缓存。所以您别用这种方法存储您公司的logo,因为它会增加您每个页面的容量。要解决这个问题,您可以把inline
image写在CSS中,尽管date:URL不会被缓存,但CSS是可以被缓存的,Inline CSS Images
:http://stevesouders.com/hpws/inline-css-images.php下面就是接上面的例子,为每个SPAN加上inline
image:
.home { background-image:
url(data:image/gif;base64,R0lGODlhHwAfAPcAAAAAAIxKA...);}
.gift {
background-image:
url(data:image/gif;base64,R0lGODlhHwAfAPcAAAAAAABCp...);}
.cart {
background-image:
url(data:image/gif;base64,R0lGODlhHwAfAPcAAAAAADlCr...);}
.settings {
background-image: url(data:image/gif;base64,R0lGODlhHwAfAPcAAAAAA...);}
.help
{ background-image:
url(data:image/gif;base64,R0lGODlhHwAfAPcAAAAAALW1t...);}
PHP的file_get_content函数可以很轻松的实现inline image的那串base64的图片编码。我上面的例子就是用的这个函数:
.home { background-image:
url(data:image/gif;base64,
<?php echo
base64_encode(file_get_contents("../images/home.gif")) ?>);}
.gift {
background-image: url(data:image/gif;base64,
<?php echo
base64_encode(file_get_contents("../images/gift.gif")) ?>);}
Combined
Scripts and Stylesheets | 15
.cart { background-image:
url(data:image/gif;base64,
<?php echo
base64_encode(file_get_contents("../images/cart.gif")) ?>);}
.settings {
background-image: url(data:image/gif;base64,
<?php echo
base64_encode(file_get_contents("../images/settings.gif")) ?>);}
.help {
background-image: url(data:image/gif;base64,
<?php echo
base64_encode(file_get_contents("../images/help.gif")) ?>);}
我们比较一下上面的几个例子,image maps与CSS Sprites的响应时间基本上相同,但比使用各自独立图片的方式要快50%以上。用inline
image与CSS组合的方式虽然增加一个HTTP请求,但是它可以以样式表的形式被缓存。
Combined Scripts and
Stylesheets
尽量使用独立的js和样式表文件 现在的前端开发少不了JavaScript和CSS,但我们还是建议:使用外部的js和css文件引用的方式,因为这要比直接写在页面中性能要更好一点(细节会在Chapter
8中讨论)。但是如果你滥用这条规则,把你的代码切割成了很多个小文件,那只会增加更多的HTTP请求而影响性能。
表1-1是10大网站首页中js和样式表的数量表,它们的样式表都不多,但js显然还有优化的余地。

下面的例子中,你会发现用独立的一个js比用多个js文件组成的页面载入要快38%.
Separate Scripts
http://stevesouders.com/hpws/combo-none.phpCombined
Scripts
http://stevesouders.com/hpws/combo.php
有的人可能会习惯于按功能或模块来把js分成各种不同的文件,但以我的经验,如果一个网站的页面引用大量的js文件,那应该要分析一下这些分类是否真的便于管理。
结论
这一章节介绍了我们在Yahoo!用到的以减少HTTP请求数量的技术方法。也是对访问网站很重要的一条法则。它会提高用户第一次访问您网站时的体验速度。而更快的访问速度将会使用户更愿意常回头访问您杰作。
相关章节:
翻译:High Performance Web
Sites(1)-Chapter A翻译:High Performance Web
Sites(2)-Chapter B
受关注文章