黄金法则:只有10%-20%的响应时间是花费在了HTML文档的下载上,其余时间花在了下载页面中的所有组件。

HTTP 概述

压缩:

Accept-Encoding:浏览器可以使用这个头来声明它支持的压缩 Content-Encoding:服务器使用这个头来确认响应内容是如何压缩的

条件GET请求:

如果浏览器在其缓存中保留了组件的一个副本,但是并不确定它是否仍然有效,就会生成一个条件 GET,如果确认缓存有效,就使用缓存。缓存的有效期是基于响应头中的 Last-Modified 头来决定的,浏览器在进行缓存的时候,会记下这个时间,下次遇到同样 url 的请求时候,会包含一个 If-Modified-Since 的头将本地缓存的最后修改时间发送给服务器。

如果服务器发现这个缓存仍然有效,自己在这之后没有修改过这个文件,那么就会发送 304 Not Modified 的状态码,并且不包含响应体。浏览器就会根据这个响应来确认可以使用缓存。

Expires

条件 GET 和 304 响应可以加快页面加载,但是每个资源仍然和服务器进行了一次交互,以确认缓存有效。使用 Expires 可以免去这一次确认。浏览器看到响应中含有这个头信息,就会把该信息和缓存内容保存起来,这个头信息告诉浏览器这个资源的有效期限,如果在这个有效期内,浏览器就可以放心大胆地使用该资源。

Keep-Alive

HTTP 构建在 TCP 之上,在 HTTP 的早期实现中每个 HTTP 请求都要打开一个 socket 连接,这样做的效率很低,因为一个 web 页面中很多内容是来自同一个服务器的,可以在一次链接中传输多个资源无疑是高效的。持久连接解决的这个问题。浏览器和服务器使用 Connection 头来指出对 Keep-Alive 的支持。浏览器和服务器可以发送 Connection:Keep-Alive 和 Connection:close 来保持或者关闭连接。

规则一:减少HTTP请求

减少文档中引入资源的数量,合并小的脚本文件,样式表,图片等等。

图片地图

使用 image 和 map 标签构成图片地图,可以显著减少 HTTP 请求数量,不需要为每个链接添加一个背景图片。但是这个应用好像很少见到。

CSS Sprites

比上面的图片地图更加灵活,而且合并后的图片虽然包含一些空白,但是大小并不并未合并前大,因为合并在一幅图片上省去了在每个图片中都包含的颜色索引表,格式信息等等附加内容。

内联图片

使用 data:url 的模式来内联图片,图片信息就在 url 中,可以随文档一起传输减少了 HTTP 请求。这个 data:url 模式是在1995年提出的,数据格式如下:

data:[][;base64],

注意:IE7以及IE7- 不支持该方法

base64 编码会增大图片的大小,导致整体的下载量增大,所以要慎重使用。另外使用该方式导致资源不会被缓存下来,一个好的解决方案是讲 base64 编码的内容放在样式表内,样式表是会被缓存的。

合并脚本和样式表

在投入生产环境之前对样式表和脚本进行压缩。

规则二:使用内容分发网络

将静态资源部署在 CDN 上有助于消除地域上的差异,提高其他地方用户的响应速度。

规则三:添加 Expires 头

Expires:Mon, 15 Apr 2024 20:00:00 GMT

这个响应头告诉浏览器这个时间前,该资源都是有效的。

Expires 会有一些不好的地方,首先需要服务器和客户端的时间是同步的,另外真的这一天到来了,还要为资源重新提供一个新的时间。

HTTP1.1版本,提供了 Cache-Control , 使用 Cache-Control 可以克服这个问题。Cache-Control 使用 max-age 指令来指定组件可以被缓存多久。它以秒为单位定义了一个更新窗,如果从组件被请求开始少于这个秒数,那么可以使用这个资源。否则过期,使用这个方法也可以长时间地缓存一个资源。

Cache-Control:max-age=315350000

如果一个识别 HTTP1.1的客户端同时看到了 Cache-Control: max-age 和 Expires 那么它会使用 Cache-control。对于 不支持 HTTP1.1的客户端(几乎没有了吧)那么它会使用 Expires。

规则四:压缩组件

压缩是如何工作的

web客户端通过 Accept-Encoding 头来表示对压缩的支持。

Accept-Encoding:gzip,deflate

服务器看到这个信息,就会启用列表中列出的压缩方式进行压缩,然后将采用的压缩方法写在响应头部发送给客户端。

Content-Encoding:gzip

gzip 是 GNU 开发的一个免费的格式,使用相当广泛。

压缩什么

文档,样式表,脚本等文本值得进行压缩,但是图片、PDF等内容则不适合被压缩,因为它们以及被压缩过了。压缩带来的成本是服务器的压力增大,客户端需要解压缩也会带来消耗。根据经验通常对大于 1kb 的内容启用压缩,这在 web 服务器中可以自行配置。

代理缓存

当客户端通过代理来向服务器发送请求的时候,一些问题就产生了。当一个不支持压缩的客户端通过代理向服务器发送了请求,这个时候服务器返回的是一个未压缩的版本,然后代理将其缓存了起来,下一次一个支持压缩的客户端通过代理向服务器发送请求的时候,代理会直接返回未压缩的版本。这就导致后面的请求内容是未经压缩来传输的。

如果反过来,先由支持压缩的客户端发送请求,后由不支持压缩的客户端发送请求,这个时候问题就严重了不少。解决方案是使用HTTP响应头信息中的 Vary 字段。服务器通过这个字段告诉代理,通过请求头部信息来决定缓存的响应。

Vary:Accept-Encoding

上面这个响应头告诉代理,给包含不同的 Accept-Encoding 内容的请求保持一个缓存,此后根据这个请求头信息来提供缓存内容。

规则五:将样式表放在顶部

逐步呈现

把样式表放在文档底部会导致浏览器阻止页面逐步呈现,这会导致用户看不到页面上内容的,在浏览器等待文档底部的样式表的时候,会延迟显示任何可视化组件。在 IE 中会导致白屏现象。

将样式表放在文档的顶部则能很好地解决白屏现象,使页面逐步呈现。

在样式表加载完毕之前构建呈现树是一种浪费,在样式表加载完成之前显示会导致无样式内容的闪烁,如果不显示会导致白屏。具体的表现不同的浏览器有不同的处理方案。

另外 w3.org 中 HTML 的规范显示 Link 标签只应该出现在 head 中,之所以写在其他位置也可以是因为浏览器为了兼顾一些不安规范写的 HTML 文档。

规则六:将脚本放在底部

脚本的执行会阻塞文档剩余内容的呈现。可以看这个页面 http://stevesouders.com/hpws/js-middle.php 看看实际效果。将脚本放在文档上方,还会阻塞浏览器并行下载。也就是浏览器会停止页面的解析,文档剩余部分的资源不会开始下载,直到脚本加载完,并执行完成。

把脚本放在文档最下方,这是最佳实践。

规则七:避免 CSS 表达式

对于 IE ,其支持 CSS 表达式,如下:

width:expression(document.body.clientWidth < 600 ? “600px” : “auto”);

其他浏览器会忽略该属性,但是 IE 认识。这个写法的性能低下之处,在于其更新频率远远超出你的预估。它不单单会在页面大小改变的时候求值,用户滚动页面,鼠标移动这都会进行求值,不信?使用 IE 浏览器打开这个页面 http://stevesouders.com/hpws/expression-counter.php 试试。页面的一次滚动都可能导致求值几千上万次。

结论:避免使用 CSS 表达式,必要的时候使用 javascript 处理。

规则八:使用外部 Javascript 和 CSS

使用分离的样式表和脚本可以使其被浏览器缓存,本站的其他页面能够重用该文件。

规则九:减少DNS查找

客户端遇到一个域名首先要做的就是进行 DNS 解析,这个过程会消耗一定的时间,可以试图使用同一个地址来加载资源,但是浏览器可能限制同时对一台主机进行的 HTTP 请求数量,所以在主机名数量,与 DNS 查找这两者之间要找到一个平衡点。

另外使用 Keep-alive 也可以有效减少 DNS 查找。

规则十:对脚本进行压缩

对脚本启用压缩

规则十一:避免重定向

最常见的重定向状态码是 302,(304并不是重定向),从定向的响应头中包含了 Location 字段,这个告诉浏览器转而访问另外一个页面。HTML文档中也可以进行重定向:

<meta http-equiv="refresh" content="1; url=http://baidu.com">

上面信息会在1秒后跳转至baidu。

关于重定向的一个细节:

缺少结尾的斜线

当 url 是对应于主机上的一个 目录的时候,主机会发送 301 重定向状态码。所以注意在写 url 的时候,路径要尽可能地指明要访问的资源。

在地址栏输入:http://www.baidu.com 这个不会重定向,因为浏览器在发送请求的时候一定要有一个路径,如果没有指明,那么就会使用 http://www.baidu.com/ 也就是根目录。

你可以在命令行中尝试下列命令:

curl -g sports.qq.com/nba

就可以看到 腾讯服务器发回来的重定向内容。

当然了,也可以使用 Telnet 来看看HTTP报文。

重定向还用于跟踪流量,对于搜索引擎,可能进行重定向将用户转向搜索结果所在页面。

重定向也用于美化 url ,一个短的 url 总是更好记忆一些。

规则十三:配置 ETag

Etag(实体标签)是web服务器和浏览器用于确认缓存组件有效性的一种机制。Expires 头指示了资源的过期时间。最新修改时间(Last-Modified)则说明了资源最后的修改时间,这在条件 GET 的时候很有用(If-Midified-Since)。

而实体标签则用来唯一标识一个资源。如果你的组件必须通过最新修改时间之外的东西来验证的话,那么使用 Etag 是一个很有用的方法。

Etag 的合理使用可以避免资源被重新请求,因为就算是同一个资源,如果响应中每次都给出了不同的 Etag ,这个时候也不会发送 304 ,而是重新加载资源。所以配置或者移除Etag。

规则十四:使 Ajax 可以缓存

同样地给 Ajax 请求设置一个长的 Expires 头,另外可以使用 主动请求 在用户未访问数据之间请求数据。