【导读】:在互联网上获取内容是相当耗时和昂贵的:大的响应需要在客户端和服务器之间有很多次通信来回,这延缓了他们被浏览器使用和处理的时机,同时也增加了访问者的数据流量开销。因此,缓存和重用已获取的资源成为了性能优化很关键的一方面。 本课内容
好消息是每个浏览器都自带HTTP缓存的实现!我们所要做的就是保证每个服务器响应提供正确的HTTP头指令信息来指导浏览器何时进行缓存以及缓存保留多长时间。 注意:
服务器返回一个响应的同时也发出了一个HTTP头的集合,用来描述响应的类型,长度,缓存指令,验证令牌(validation token)等。举个例子,在上图的交互中,服务器返回了一个1024字节的响应,指示客户端将它缓存120秒,提供了一个验证令牌(“x234dff”),它可以在响应过期之后用来验证资源是否更新过。 使用ETags验证缓存的响应学习重点
让我们假设浏览器在获取一个资源120秒之后又对相同的资源发出请求。首先,浏览器会检查本地缓存并找到之前的响应,不幸的是这个响应已经“过期”不能用了。这时,浏览器原本可以直接发出一个新的请求来获取完整的响应,但是这并不是最高效的,因为如果这个资源没有被更改过,我们就没有理由再去下载一个跟缓存中完全一样的资源。 这就是ETag头信息中放入验证令牌所要解决的问题:服务器生成并返回一个随机令牌,通常是文件内容的哈希(hash)或者其他指纹码(fingerprint)。客户端不需要知道指纹码是如何生成的,它只需要将它在下一个请求中发回给服务器:如果指纹码仍然一致,说明资源没有被修改,我们就可以跳过下载。
在上面的例子中,客户端自动在“If-None-Match”请求头中提供了ETag令牌,服务器端针对当前的资源检查令牌,如果没有被修改过,就返回“304 Not Modified”响应,告诉浏览器,缓存中的响应没有被修改过,可以延用下一个120秒。注意到我们不需要再次下载响应——这节约了时间和带宽。 作为一个Web开发人员,你该如何利用高效的加签(revalidation)?浏览器已经为我们做了很多:它自动检测验是否已经拥有验证令牌,它会将验证令牌附加到发出的请求上,会根据服务器的响应在必要的时候更新缓存时间戳。事实上,唯一留给我们要做的就是保证服务器提供必要的ETag令牌:阅读你的服务器文档并设置必要的配置项。 小贴士:HTML5 样板项目包含了所有最流行的服务器的配置文件样例,并为每一个配置项都提供了详细的注释:找到你最喜欢的服务器,查找适当的设置项,然后拷贝/确认你的服务器使用了推荐的设置。 Cache-Control学习重点:
最好的请求是不需要和服务器交互的请求:一个本地的响应拷贝可以帮助我们消除所有的网络等待和数据传输费用。为了达到这个目的,HTTP规范允许服务器返回一系列不同的Cache-Control指令来控制一个响应在浏览器或者其他中继缓存中如何被缓存以及缓存多久。 小贴士:Cache-Control头定义在HTTP/1.1规范中,取代了之前用来定义响应缓存策略的头信息(如:Expires)。所有的现代浏览器都支持Cache-Control,因此使用它就够了。
“no-cache”和“no-store”“no-cache”指明当前返回的响应在后续相同的URL请求时必须先与服务器确认响应是否被修改,之后才能被用作后续请求响应的缓存。因此,如果我们有一个合适的验证令牌(ETag),no-cache会增加一次与服务器的通信来验证和确认缓存的响应,但是可以避免重复下载不曾更新的响应。 相比之下,“no-store”更加简单,它直接禁止所有的浏览器和中继缓存存储任何版本的响应——比如:一个包含了个人隐私信息或者银行信息的响应。每次用户请求这些资源的时候,都会发送一个请求到服务器并且下载完整的响应内容。 “public”和“private”如果响应被标志为“public”,那么它是可以被缓存的,即使它有HTTP认证信息,甚至响应状态码不是正常的可缓存。大多数情况下,“public”不是必须的,因为明确的缓存信息(比如“max-age”)已经说明响应是可以被缓存的。 相比之下:“private”响应可以被浏览器缓存,但是通常只为单个用户缓存,一次不允许任何中继缓存件对其进行缓存——比如,一个包含用户死人信息的HTML页面可以被这个用户的浏览器所缓存,但是不能被CDN缓存。 “max-age”这个指令指明获取的响应从当前请求开始允许被重用的最大时间限度(单位为秒)。例如:“max-age=60”说明响应在接下来的60秒内可以被缓存和重用。 定义最优Cache-Control策略根据上面的决策树来为你的应用使用的单个资源或资源集设置最优的缓存策略。理想情况下,你应该旨在在客户端尽可能长时间地缓存尽可能多的响应内容,并且为每个响应提供验证令牌来启用快速验证。
根据HTTP文档,在排名最高的300,000个网站中(Alexa评分),几乎下载的响应中有一半可以被浏览器缓存,这在重复的页面浏览和访问来说是一个巨大的节省!当然,这并不意味着你特定的应用就一定有50%的资源可以被缓存:有些网站可以缓存90%以上的资源,而有些则有很多私人的或者时间敏感的数据以至于根本不能被缓存。 审查你的页面来识别出哪些资源可以被缓存,并确保他们返回正确的Cache-Control和ETag头信息。 废弃和更新已缓存的响应学习重点:
一个浏览器创建的HTTP请求会首先被路由到浏览器的缓存来查看是否有存在有效的响应缓存来直接答复请求。如果有匹配的响应,它就会直接从缓存中读取,这样我们就省去了传输所带来的网络等待和数据流量。然而,我们该如何更新或者废弃一个已缓存的请求? 举例来说,假设我们已经告诉我们的访问者缓存我们的CSS样式文件24小时(max-age=86400),但是我们的设计师刚刚提交了一个更新,我们希望所有用户都能使用。那么我们该如何通知我们所有的访问者他们的缓存拷贝已经过时了,需要更新缓存?这是一个欺骗性的题目——实际上我们我们做不到,至少在不改变资源URL的情况下做不到。 一旦响应被浏览器缓存,被缓存的版本一直被使用,直到它不再新鲜,这由max-age或者expires指定,或者它因为某些原因从缓存中被删除——比如用户清理浏览器缓存。因此,不同的用户在页面被构造的时候可能使用的是不同的版本的CSS文件;最新获取资源的用户会使用更新过的版本,而缓存过之前版本(依然有效)拷贝的用户会继续使用老版本的响应。 所以,我们如何才能兼得鱼和熊掌:客户端缓存和快速更新? 很简单,我们可以在资源内容更新时改变资源的URL来强制用户下载最新的响应。典型地:可以通过在文件名上嵌入文件的指纹码,或者版本号来实现——比如 styoe.x234dff.css。 能够定义单个资源级别的缓存策略让我们能够定义“缓存层级”,这让我们不但能够控制缓存有效的时间,而且还能控制新的版本何时能被访问者看到。例如,我们一起分析一下上面的例子:
结合使用ETag,Cache-Control,和唯一的URL允许我们提供最佳的方案:长久的过期时间,控制哪些响应可以被缓存,同时按需更新。 缓存备忘录没有一个固定的最佳缓存策略。你要根据你的通信模式,服务的数据类型,和应用特定的数据刷新需求等来定义和配置最合适的单个资源级别的设置,以及整体的“缓存层级”。 在定义缓存策略时使用的一些技巧和技术:
原文链接: web fundamentals 翻译: 伯乐在线 - 陈鑫伟 |