减少HTTP请求

Dec 25, 2010

    这篇文章的一个基调是根据Yahoo! Exceptional Performance Best Practices中第一条规则"减少HTTP请求".         

为什么需要减少HTTP请求次数?

    当你打开一个网页时,浏览器就会向服务器端发送请求,这些请求包括有图片,CSS,Javascript和Flash等等,然后服务器对这些请求做出响应,将对应的资源发回给客户端.这个过程中用户需要等待这些响应,而服务器需要分配资源来处理请求,对于一些小型的网站,浏览的人少,那么对应的服务器的压力也不会太大,响应速度也不至于太慢(当然这也跟网速有关).但是对于大型的网站,每秒中浏览人的量是非常大的,那么服务器的所承受的压力也会变大,到了一定程度,就会出现处理不过来的情况,对于用户而言,就是看到一张空白的网页,难以提供良好的用户体验.这里可以参考 "Performance Research, Part 1: What the 80/20 Rule Tells Us about Reducing HTTP Requests" by Tenni Theurer on the Yahoo! User Interface Blog.
Table 1 shows popular web sites spending between 5% and 38% of the time downloading the HTML document. The other 62% to 95% of the time is spent making HTTP requests to fetch all the components in that HTML document (i.e. images, scripts, and stylesheets). The impact of having many components in the page is exacerbated by the fact that browsers download only two or four components in parallel per hostname, depending on the HTTP version of the response and the user's browser. Our experience shows that reducing the number of HTTP requests has the biggest impact on reducing response time and is often the easiest performance improvement to make.
    既然关键提高性能的关键之一是在于减少HTTP请求次数,那么什么东西会引起这些请求了? 常见的便是下面几个方面:CSS文件,Javascript文件和图片.     对于CSS文件,我们可以使用将很多零散的CSS文件合并并且压缩(比如使用Gzip,经过压缩之后大小只有原来文件的70%-80%,相当客观的成绩),然后在传送到浏览器端.在Rails中,注意到在application.html的title标签中
           stylesheet_link_tag :all, :cache => true
米高说这个这个all的属性使得Rails在产品环境下将所有位于public/stylesheets目录的css文件进行合并成一个叫all.css文件,可以减少HTTP请求的次数,很有用的一个功能     对于Javascript文件,同样的也可以对其进行压缩,比如常见的JQuery就有压缩之后的版本jquery.min.js.     对于上面二者之前,大概可以归纳出减少HTTP请求次数可以通过合并压缩的办法来达到.对于图片是不是也可以借鉴了?         

CSS Sprite

    减少HTTP请求次数中有一条:
CSS Sprites are the preferred method for reducing the number of image requests. Combine your background images into a single image and use the CSS background-image and background-position properties to display the desired image segment.
    CSS sprite的想法很简单,就是将零零碎碎的图片合并为一张图片,并通过background-position来在大图片中定位到自己需要显示的区域.     对于下面实现一个简单的 一般的我们习惯将弄十张这样的小图片:
ul li a#doc_link{
background-image:url("../images/doc_grey.png");
}
ul li a#doc_link:hover{
background-image:url("../images/doc.png");
}
...
    但是我如果使用CSS Sprite的话,就是简单一张“大”图片:     在这个过程中,我发现合并这些图片对于我这样一个photoshop盲来说,很吃力,所以我很明智请求别人帮忙.而且合并完成之后还需要一张一张图片计算这偏差,更是显示的有点"得不偿失". 所以大牛Steve Souders在这篇SpriteMe makes spriting easy中宣布了SpriteMe,而且他此文中把SpriteMe和另一个制作图片精灵的工具CSS Sprite Generator做了一个比较. 个人感觉SpriteMe非常强大,特别是它有一个选项"Share your savings"能提交你使用Sprite之后数据的变化,包括文件大小,请求的次数浮动等等. 但是有一个问题是当你的页面没有出现"suggestions"时,这个"new sprite"根本不工作,这时只能使用CSS Sprite Generator了.     那这个时候我想到能否将css sprite跟rails集成起来了,一搜果然有人提供了这样的支持.黄志敏在其博客CSS Sprite Best Practice中就有提到他自己写的一个支持.你可以在这里了解到更多关于使用和安装的细节:Use css sprite automatically.经过半个小时的摸索,我发现它的好处在于它提供了更小粒度对图片操控的能力,同时接管了很多繁琐的任务,比如如何合并图片,按照哪种方式来产生对应的CSS等等. 但是也有问题在首先它的安装过程过于纠结,它需要依赖rmagick,但是rmagick装起来有点麻烦(MacPorts is the devil, and shouldn’t be installed just for installing ImageMagick).另外一个是对于png图片的生成出现了透明度丢失的现象,对于JPG图片却是正常的.     ps:Dave Shea在Sprite Optimization中提到了他在拼接png图片时,并不会使用Adobe compressor,而是其他的压缩工具Pngcrush 或者 PngThing ,我不太清楚黄志敏在实现css_sprite插件用得是什么样的压缩工具.     那是不是CSS Sprite没有负面效应了? 你肯能想到了,如果这张Sprite图片太大了,那么浏览器所需要的内存会爆了?     确有,Andy King在CSS Sprites: How Yahoo.com and AOL.com Improve Web Performance提到在iphone上,过大的图片会占用手机本不是很大的内存,可能导致一些问题,并指出:
To maximize accessibility and usability, CSS sprites are best used for icons or decorative effects.
    Firefox工程师Vladimir就在To Or Not To Sprite 文章提到了使用CSS Sprite有可能会显示大图片时肯能产生的内存问题. 这里也顺便引用他关于使用CSS Sprite应该注意什么时的一段话:
There are cases where CSS sprites are clearly beneficial; the main example is combining a bunch of small similarily-sized images into a single one.  For example, a bunch of 16×16 icons used as indicators for various things on your site, or a bunch of 32×32 icons used as category headers or similar.  But combining images with vastly different dimensions, especially when there are skinny and tall images in the same sprite as wide and short ones is almost never a good idea.
        

Inline Images

    减少HTTP请求次数中还有有一条: Inline images use the data: URL scheme to embed the image data in the actual page. This can increase the size of your HTML document. Combining inline images into your (cached) stylesheets is a way to reduce HTTP requests and avoid increasing the size of your pages. Inline images are not yet supported across all major browsers.     
base64-Encoded Data URIs
    对于这个我的理解是使用一中更高效的encode方式将图片(或者其他东西,比如HTML)的内容字节转换成一种更精短的字节,这些转换后的字节足够小以致于我们可以直接在CSS文件中描述出来,同时定义相应的条约让浏览器在解析的时候能根据这个条约将字节反向还原过来. 要理解这个,得弄清楚URL跟URI的区别,在Nicholas C. Zakas的文章Data URIs explained中有详细说明它的工作原理,强力推荐. 想象一下在一个图片非常多网页中,这个HTTP请求的次数将会减少多少,因为每一张图片都不需要发送任何请求了.这样以来相对CSS Sprite ,Nicholas都甚至喊出来"Data URIs make CSS sprites obsolete" 的口号. 但是你会疑问说,那么你也看到了CSS文件会变得越大阿?不会得不偿失吗?     那这个确实你可以通过Gzip对CSS文件进行压缩,压缩之后大小就跟你原来写图片引用的CSS文件是差不多的,所以这个不是一个问题.但是它的浏览器兼容怎么样了?     
Web Browser Support
    Data URIs are supported in:      Firefox 2+      Safari – all versions      Google Chrome – all versions      Opera 7.2+      Internet Explorer 8+     你有可能看到了,它并不支持浏览器(MOSG都还是很不错的),特别是IE8之前的系列.IE是一个怪胎,所以如果你想在IE8之前的ie浏览器中支持URI的话,可以参考MHTML – when you need data: URIs in IE7 and under. 当然Data URI依然面临一些问题,最突出的便是相对于原来图片可以在客户端被缓存,而现在描述图片的内容都在CSS文件中,一旦对于CSS文件有最微小的改变,也会导致整个代码需要重新下载,比较麻烦.     使用:     IE本来就不应该成为你忌讳某个工具的原因,所以及时你不得不这样,Nicolas其实已经设计好了这样一个工具cssembed,它可以让你选择输入的CSS文件,然后遍历文件讲图片引用转码成Base64.你完全不用担心IE的问题,它都有处理好了. 下载好JAR文件后,你可以通过这样的调用
java -jar cssembed-0.3.2.jar styles.css > styles-base64.css
    这样以来就可以将style.css文件中所有的图片引用转码成为base64格式,并输出到style-base64.css文件夹中,就那么简单.
ul li a#css_link{
   background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAA..省略...)
}
    使用传统方式:  Style.css 大小没有压缩:4kb 加上10张小图片的大小:137KB ,总共是141KB,10次请求图片     使用CSS sprite: Style.css大小:4kb 加上Sprite图片大小:85kb ,总共是89KB, 1次请求图片     使用Data-URI Base64没有压缩:194kb 使用YUI Compressor压缩之后貌似仍然是185kb,0次请求图片.         CSS Sprite和Data-URI:
CSS sprites were a solution to the problem of multiple HTTP requests to download multiple images. Data URIs allow you to embed images directly into your CSS files, solving the same problem in a much more elegant and maintainable way.
    最后介绍一下Rails下强大的库Jammit,它不仅支持对CSS和Javascript的像YUI compressor类似的压缩功能,同时它还支持Data-URI / MHTML image and font embedding.         

引用

    Dave Shea在A List Apart的博客: CSS Sprites: Image Slicing’s Kiss of Death     在09年的跟进CSS Sprite更新了一篇博客:Sprite Optimization     Chris Coyier在CSS Tricks的非常棒的一篇文章 CSS Sprites: What They Are, Why They’re Cool, and How To Use Them     CSS-Tricks CSS Sprite Screencast: How to Use CSS Sprites     Robert Nyman;How To Reduce The Number Of HTTP Requests     Rob Larsen的文章: Why Front End Performance Matters to Everyone, Not Just the High Traffic Giants     黄志敏(flyerhzm)关于CSS sprite best practice 的PPT: Css sprite best practices     提高性能另外一个关键在于异步加载提高并发,绕过传统的阻塞模型,实现网页的迅速加载.以后会继续提到"non-blocking script",有意者请看Steve Souders的ControlJS系列和Nicolas的Thoughts on script loaders.         嗨,这一天我的Reader可是多了不少的订阅阿~~ Update:关于更多DataURI的内容可以参考:Wiki的data URI scheme