减少HTTP请求
图片地图
// 示例代码
<img usemap="#map1" src="...">
<map name="map1">
<area shape="rect" coords="0,0,31,31">
...
</map>
CSS Sprites
合并图片,使用CSS背景定位。
内联图片
通过使用data:URL模式可能在web页面上包含图片但无需任何额外的HTTP请求。
data:URL除了可以用于内联图片,还可以用在任何需要指定URL的地方,包括script和a标签。
缺点是不受IE支持,另外可能存在数据大小上的限制。
由于data:URL是内联在页面上的,在跨越不同页面时不会被缓存。在这种情况下,聪明的作法是使用CSS并将内联图片作为背景。将该CSS规则放在外部样式表中,这意味着数据可以缓存在样式表内部。
合并脚本和样式表
在开发环境下分模块,在生产环境下合并脚本和样式表。
使用内容发布网络(CDN)
只有10% ~ 20%的最终用户响应时间花在下载HTML文档上。其余的80% ~ 90%时间花在了下载页面中的所有组件上。
使用CDN将组件Web服务器分散开(组件服务器离用户距离越近,响应时间越短),不仅能达到响应时间大幅减少的目的,而且还很容易实现。
添加Expires头
Web服务器使用Expires头来告诉Web客户端它可以使用一个组件的当前副本,来减少HTTP请求。
Expires: Mon, 15 Apr 2024 20:00:00 GMT
Cache-Control: max-age
Expires头使用一个特定的时间,它要求服务器跟客户端严格同步。HTTP1.1引入了cache-control头来克服Expires头的限制。
Cache-Control使用max-age指定组件被缓存多久,一个长久的的max-age头可以将刷新窗设置为未来10年。
Cache-Control: max-age=315360000
Expires-Default
Apache模块的mod_expires提供Expires-Default指令同时向响应中发送Expires头和Cache-Control max-age头。(跨浏览器改善缓存的最佳解决方案)
当我们需要更改缓存中的文件时,最有效的解决方案是修改其链接,这样,全新的请求将从服务器下载最新的内容。雅虎的做法是将版本号嵌入文件名中。
当我们没有使用Expires头时,浏览器也会有缓存,只不过浏览器会向服务器发送一条GET请求,确定是否要使用缓存中文件。
压缩组件
从HTTP1.1开始,Web客户端可以通过HTTP请求中的Accept-Encoding头来表示对压缩的支持:
Accept-Encoding: gzip, deflate
如果Web服务器看到请求中有这个头,就会使用客户端列出来的方法中的一种来压缩响应。Web服务器通过响应中的Content-Encoding来通知Web客户端:
Content-Encoding: gzip
代理缓存
可以通过设置Vary: Accept-Encoding让代理服务器缓存压缩和未压缩的响应,以满足不同浏览器的需求(浏览器是否支持如gzip)。
边缘情形
发送内容到不支持它的客户端,忘记将压缩内容声明为已经进行了gzip编码等,页面都会被破坏。
- 如果你的网站用户很少,并且他们处在一个小圈子中(例如,他们在一个intranet中,或者都使用Firefox 1.5)边缘情形浏览器就不需要太多关注。可以压缩内容并使用Vary:Accept-Encoding。这样可以通过减少组件的大小和利用代理缓存来改善用户体验。
- 如果你更注意带宽开销,可以和前一种情况一样——压缩内容并使用Vary: Accept-Encoding。这降低了服务器端的带宽开销并提升了代理处理的请求数量。
- 如果你拥有大量的,多变的用户群,能够应付较高的带宽开销,并且享有高质量的名声,请压缩内容并使用Cache-Control: Private。这禁用了代理缓存但避免了边缘情形缺陷。
将样式表放在顶部
在IE中,将样式表放在底部会导致出现“白屏”。
一个style块可以包含多个@import规则,但@import规则必须放在所有其他规则之前。
<style>
@import url("styles2.css");
</style>
@import规则也有可能会导致白屏现象,因为使用@import规则会导致组件下载时的无序性。即便把@import规则放在文档的HEAD标签中也是如此。
无样式内容的闪烁
把样式表放在最后的情况:页面加载时,文字首先显示,然后是图片,最后在样式表正确地下载并解析之后,已经呈现的文字和图片要用新的样式重绘了,这就是“无样式内容的闪烁”。而白屏现象正是对这个问题的弥补。
将脚本放在底部
下载脚本时并行下载实际上是被禁用的,即使使用了不同的主机名,浏览器也不会启动其他的下载。其中一个原因是,脚本可能使用document.write来修改页面内容,因此浏览器会等待,以确保页面能够恰当地布局。
在下载脚本时浏览器阻塞并行下载的另一个原因是为了保证脚本能够按照正确的顺序执行。如果并行下载多个脚本,就无法保证响应是按照特定顺序到达浏览器的。
避免CSS表达式
使用外部JavaScript和CSS
内联的JavaScript和CSS可以减少额外的HTTP请求,但是使用外部的JavaScript和CSS有机会被浏览器缓存起来,在这种情况下,HTML文档会比使用内联的HTML文档小,而且不会增加HTTP请求的数量。
使用外部JavaScript和CSS能提高组件重用率,并且易维护。
对于很多网站的主页来说,使用内联JavaScript和CSS会是更好的选择。然后在主页加载完成后下载其余页面的外部JavaScript和CSS,使其被浏览器缓存起来。
动态内联是指服务器根据cookie生成有内联样式的页面或有外部链接的页面。
减少DNS查找
DNS是将域名映射到IP地址,而这个查找过程需要时间。
通常大型网站会使用不同的主机,以提供更多的并行下载,这样页面相对能够更快地展示给用户。但是越多的主机,意味着需要更多的DNS查找。
精简JavaScript
避免重定向
重定向引起的延迟也很严重,因为它延迟了整个HTML文档的传输。在HTML文档到达之前,页面中不会呈现任何东西,也没有任何组件会被下载。
URL的结尾没有斜线会导致一次重定向,这个是可以人为避免的。但是主机后缺少斜线时不会发生重定向,如http://www.yahoo.com,你会发现浏览器地址栏会在结尾自动添加斜线。这是因为浏览器在进行GET请求时必需指定路径。如果没有路径,它就会简单的使用文档根(/)
删除重复脚本
在一个页面中两次包含同一个JavaScript文件会损伤性能,因为会产生不必要的HTTP请求(IE)和执行JavaScript所浪费的时间(脚本会被多次求值)。
在IE中,被缓存的文件还会发送条件GET请求,以确定缓存中的文件是否需要更新。
配置ETag
ETag(Entity Tag,实体标签)是Web服务器和浏览器用于确认组件有效性的一种机制。
浏览器下载组件时,会将它们存储到缓存中。在后续的页面浏览中,如果缓存的组件是“新鲜的”,浏览器就会从磁盘上读取它,避免产生HTTP请求。如果组件没有过期,那它就是“新鲜的”,这取决于Expires头。
如果缓存过期了,浏览器必须检查它是否还有效,这时,浏览器会发送一个条件GET请求到服务器上进行确认。如果缓存有效,则会返回304 Not Modified的状态码。
检查组件是否有效有两种方式:
- 比较最新修改日期
- 比较实体标签
浏览器缓存在组件和最新的修改日期,下次请求时,会使用If-Modified-Since头将最新修改日期到服务器上比较
实体标签就是比较缓存中的组件与原始服务器上的组件是否匹配。ETag提供了比最新修改日期更为灵活的机制,因为它可以使用组件的一些属性来构造。使用If-None-Match头将ETag发送到服务器上比较。
ETag的问题是其对于服务器的唯一性,在服务器集群中,很难做到有效的验证。所以相同的组件在不同的服务器,收到的可能就不是304响应,而是200响应,因此会下载组件的所有数据。
ETag还降低了代理缓存的效率。代理后面用户缓存的ETag经常和代理缓存的ETag不匹配,这导致不必要的请求被发送到原始服务器。
而且If-None-Match比If-Modified-Since优先级高。如果请求中同时出现这两个头,则原始服务器会禁止返回304,除非请求中的条件头字段全部一致。实际上如果根本没有If-None-Match头反而会更好一些。
即便组件具有长久的Expires头,一旦用户单击了Reload或Refresh按钮,仍然会产生条件GET请求。
使Ajax可缓存
使Ajax可缓存,除了使用Expires头之外,最好的方式是使用查询子符串参数,如:
/ws/mail/v1/formrpc?m-GetMessage&yid=steve_souders