介绍
自从史蒂夫·索德斯(Steve Souders)开始宣传Web性能以来,我们就深深地想到,额外的HTTP请求会增加很多额外的开销,并且如果可能的话,我们应该将它们组合起来以大大减少网页的加载时间。
实际的含义是结合我们的JavaScript和CSS文件,这相对容易和直接,但更难的问题是如何处理图像。
精灵
图像精灵是视频游戏中的一个概念:其想法是将大量图像资产塞入一个文件中,并重新排列各种“视口”以一次仅查看该文件的特定片段。例如,一个包含两个图像的简单精灵可能具有一个仅查看精灵顶部(图像#1)的视口,而另一个仅查看其底部一半(图像#2)的视口。
在网络方面,这意味着这些多个请求现在已合并为一个请求。这很好,因为它既节省了额外HTTP请求的开销,又节省了每次设置映像文件头的开销。
但是使用图像精灵有一些缺点:
- 难以维护和更新:如果没有某些工具的帮助,手动编辑图像小精灵并将它们放在一起是一件很麻烦的事情
- 增加的内存消耗(可能非常大):这通常被忽略。减少传递图像的时间会以更大的内存和CPU占用空间为代价,尤其是对于大型sprite和具有大量空白的sprite。
- 渗色:对于没有太多空白来分离图像的精灵,附近图像明显渗入其他元素的可能性增加,因为在这种情况下渗色仅在iOS上可见(但在Chrome和Safari上看起来不错) )。请注意CNN徽标下的渗漏:
数据URI和Base64编码
数据URI(请参阅this,this和this)和Base64编码是紧密结合的。此方法使您可以将图像直接嵌入HTML,CSS或JavaScript中。
就像sprite一样,您可以保存HTTP请求,但是也存在一些缺点:
- base64编码使文件大小比其原始二进制表示大约大33%,这意味着需要传输更多数据(这在移动网络上可能会非常痛苦)
- IE6或IE7不支持数据URI,IE8限制最大32K
- base64编码的数据可能比二进制数据需要更长的处理时间(是否有人想对此进行研究?)(同样,这对于CPU和内存有限的移动设备可能会特别痛苦)(旁注:CSS背景-图像似乎实际上比img标签要快)
尽管数字根据内容类型而有很大差异,但“ 33%以上”的主张现已被普遍接受。这正是我想要测试的东西,尽管以一种非常有限且不科学的方式。
在测试之前,我想牢记一些未经验证的直觉(这些直觉并不完全是我自己的,但似乎是随处可见的想法)。这是我要测试之前的几个问题:
- 使用gzip压缩的base64编码是否大致等于二进制文件的原始文件大小?
- base64编码最适合小图像吗?
- base64编码最适合用于小型和简单图标,而不适合于图片和照片吗?
- 将多个文件合并在一起时,base64编码是否最佳?
我还想测试一下:压缩二进制图像数据是否有很大的不同。我知道文本可以很好地压缩,但是,例如,甚至值得用Gzip压缩JPEG文件吗?
我进行了三个测试:一个带有一组小型UI图标,一个带有一组小型照片,另一个带有一组较大的相同照片。尽管我的测试决不是广泛的,但它们确实表明在对base64进行假设时应格外小心。
只是关于表格的注释:它们正在将二进制形式(原始png或jpeg)与将在CSS样式表中显示的base64形式进行比较,并将每个表格与它们的压缩形式进行比较,这很可能是送电线。CSS表示有一些实际的声明,看起来像这样:
|
|
好,开始测试!
测试#1:赋格图标集(PNG)中的五个16×16图标
文件 |
二元 |
二进制压缩 |
CSS + Base64 |
CSS + Base64压缩 |
算盘 |
1443 |
1179 |
2043年 |
1395 |
橡子 |
1770年 |
1522 |
2478 |
1728 |
地址簿箭头 |
763 |
810 |
1153 |
948 |
地址簿惊叹号 |
795 |
848 |
1199 |
988 |
通讯录减号 |
734 |
781 |
1113 |
919 |
总 |
5,505 |
5,140 |
7,986 |
5,978 |
合并文件 |
(5,505) |
(4,128) |
7,986 |
4,423 |
- 所有数字均为字节大小
**括号中的数字表示实际但不切实际的数据。不幸的是,图像不能以二进制形式组合在一起交付。
外卖:
- 二进制文件总是较小。
- 有时,压缩会使文件变大。
- 对base64版本进行Gzip压缩,使文件大小接近原始二进制文件的大小,但这忽略了二进制文件也被压缩的事实。Gzipped二进制文件(如何将其交付给客户端)始终小于Gzipped base64映像
- 将文件组合在一起可大大减少文件大小。
实际上,开发人员有两个选择:在5个单独的HTTP请求中向用户提供5140字节,或者在一个HTTP请求中向用户提供4423字节(带有base64编码图像数据的CSS)。Base64在这里显然是赢家,并且似乎可以确认小图标的压缩效果非常好。
测试#2:五张Flickr 75×75图片(JPEG)
文件 |
二元 |
二进制压缩 |
CSS + Base64 |
CSS + Base64压缩 |
6734 |
5557 |
9095 |
6010 |
|
5379 |
4417 |
7287 |
4781 |
|
25626 |
18387 |
34283 |
20103 |
|
7031 |
6399 |
9491 |
6702 |
|
5847 |
4655 |
7911 |
5077 |
|
总 |
50,617 |
39,415 |
68,067 |
42,673 |
合并文件 |
(50,617) |
(36,838) |
68,067 |
40,312 |
外卖:
- (与测试#1相同的要点)
- 另外,使用base64编码并压缩后,照片的尺寸不会太大。这很合理。
实际上,开发人员可以在5个单独的请求中交付39,415字节,或在1个请求中交付40,312个字节。这里的文件大小差异不大,但是当我们谈论40kb时,最好是1个请求。
测试#3:五张Flickr 240×160图片(JPEG)
文件 |
二元 |
二进制压缩 |
CSS + Base64 |
CSS + Base64压缩 |
1个 |
24502 |
23403 |
32789 |
23982 |
2 |
20410 |
19466 |
27333 |
19954 |
3 |
43833 |
36729 |
58561 |
38539 |
4 |
31776 |
31180 |
42485 |
31686 |
5 |
21348 |
20208 |
28581 |
20761 |
总 |
141,869 |
130,986 |
189,749 |
134,922 |
合并文件 |
(141,869) |
(129,307) |
189,749 |
133,615 |
外卖:
- (与测试#1相同的要点)
- 较大的照片似乎使Gzipped二进制文件和Gzipped base64文件大小更接近,使差异非常小
开发人员必须在5个HTTP请求中传递130,986字节,或在一个HTTP请求中传递133,615字节之间进行选择。任何好的Souders追随者都会选择一个请求,但是在这里我会小心……
注意:事情并不总是像看起来那样
这里有一个很大的警告:在5个单独的请求中交付图像可能实际上对感知性能更有利。
为什么?因为133,615字节可将一个包中的所有内容全部交付给最终用户,而最终用户在整个过程中都将盯着空白的占位符。如果5个base64图像全部出现在一个请求中,则该请求必须先完成,然后屏幕上才会显示任何内容。所有5张图像都从空白占位符变为几乎立即从base64解码并显示在适当位置。
将此与5个最有可能并行执行的请求进行比较,并通过在下载时显示部分图像,向用户提供实际指示,指示正在下载实际图像内容(也可以尝试回退为渐进式JPEG) -实际上,任何事情都会比空白屏幕更好。这就是为什么以良好的老式方式加载图像实际上可能对感知性能有利的原因。无论如何,它们很可能将以并行方式加载,因此额外的HTTP请求实际上可能并没有真正起作用。更不用说让浏览器管理每个文件的缓存,而不是让JavaScript管理缓存并阻止您下载已经存储在localStorage或sessionStorage中的映像,会更容易。
话虽如此,通常建议将常见的UI图标放在CSS的base64中,然后让整个块都被浏览器缓存。这些通常也是干净的矢量图标,看起来压缩得很好(请参阅测试1)。
但是对于图像内容,除了HTTP请求外,没有什么要保存的,您绝对应该三思而后行地使用base64编码保存请求。是的,您将保存一些HTTP请求,实际上不会保存字节,并且用户实际上可能认为体验较慢,因为在下载图像时他们看不到图像内容。即使您节省了几毫秒的等待时间,感知性能也是最重要的。
(编辑:将“未验证的直觉”部分的措词从“未验证”更改为实际问题,以使其更清楚)
EDIT 2013年5月:添加了一个精灵泄放的示例