在上一篇文章中,我们讲到造成网络性能下降的主要原因是延迟,而非带宽。那么为了更好地让我们的网站能快速响应用户的请求,我们就必须对一些常用资源进行缓存,
以免每次用户访问都请求一遍,这也是网站性能优化比较重要的一个环节。
现代浏览器都自带了HTTP缓存实现功能,我们所要做的只是确保我们正确地设置了HTTP响应头,以便指示浏览器何时缓存响应以及缓存多久。
首先说明一点,缓存并不是说浏览器不会对该资源再次发起请求,每种缓存机制都有自己的过期策略,我们常常看到的304状态码就表面该资源已被缓存了,但浏览器不知道有没有过期,
再次使用该资源的时候,会向服务器发起请求验证一遍,如果缓存资源有效,则会返回304状态码。最坏的情况下,则是该资源过期了,还得重新发起一次请求去获取。另一种则是浏览
器连问都不问服务器,直接就从本地硬盘读取,此时可以看到浏览器给出的响应是200(from disk cache)。
ETag
假设我们有一份css资源,我们在获取该资源一段时间后又刷新了页面,这时浏览器就会检查本地资源并查找之前的响应。但遗憾的是,该响应已经过期。此时,浏览器可以直接发起请求
重新去获取一份新的数据,但这样做效率实在是太低了。若资源本身改变了还好说,但若资源本身并没有改变呢,重新获取一份数据成本太高。
这时ETag就是用来解决这个问题的,ETag可以看做是这个文件的hash值,只要文件内容没有改变,ETag值则不会变。客户端完全不需要了解ETag是怎么生成的,只需要在下一次请求的
时候将ETag带上即可(浏览器发起请求时,if-None-Match中的值就是该ETag值),若该ETag与服务器匹配,则会返回304,告诉浏览器可以使用该缓存,从而跳过再次下载过程。
Cache-Control
Cache-Control指令控制谁在什么条件下可以缓存响应以及可以缓存多久,如上面所说,有些资源我们可以直接从本地磁盘获取(from disk cache),而无需再次发起请求询问服务器,这
便是Cache-Control的作用。Cache-Control是HTTP/1.1规范中定义的,现在基本用它来取代Expires。
- "no-cahce"与"no-store"
"no-cache"表示必须与服务器确认返回的响应是否发生了变化,然后才能使用该响应来满足后续对同一资源的请求。该值可以配合ETag来使用,浏览器通过请求If-None-Match来向服务器
验证该资源是否过期,若资源未发生变化,则避免下载。
"no-store"则表示无论什么情况下,浏览器都不得缓存该资源,即便存在ETag也一样,每次都得从服务器中获取。
- "public"与"private"
如果响应被标记为"public",则即使它有关联的 HTTP 身份验证,甚至响应状态代码通常无法缓存,也可以缓存响应。大多数情况下,“public”不是必需的,因为明确的缓存信息(例如“max-age”)
已表示响应是可以缓存的。
"private"表示响应只为单个用户缓存,因此不允许中间缓存对其进行缓存。例如,用户浏览器可以缓存包含用户私人信息的HTML网页,但CDN则不可以。
- "max-age"
该值是Cache-Control中用得比较多的一个值,表示从请求的时间开始,允许获取的响应被缓存的最长时间(单位:秒)。例如,max-age设置为100,则表示该资源可以缓存100秒,100秒后,再次
请求该资源,浏览器则会携带ETag去询问服务器该资源是否过期。在该资源未过期时,访问该资源,浏览器则从本地磁盘获取,不会发起任何网络请求。
定义最佳Cache-Control策略
如上图所示,如果资源不需要缓存,我们设置Cache-Control的值为no-store;如果资源需要缓存且每次都得向服务器验证,则为no-cache;如果中间设备可以缓存,设置为public,中间设备不
可缓存,设置为private;如果缓存有时间限制,我们设置max-age的值。private与public的值可以与max-age同时使用,中间用分号隔开。
Expires
Expires头指定了一个日期/时间,在这个日期/时间之后,HTTP响应被认为是过时的,一个无效的日期,例如0也表示该资源过时。如果还设置了max-age,则该请求头会被忽略。
总结
使用缓存最好的方式就是ETag配合Cache-Control,并设置相应的max-age,一般情况下,不再建议使用Expires头。如果资源许久都不更新,max-age可以设置比较长的时间,但是这样会有个问题就是
在缓存期间,该文件发生改变,但相应的客户端却还是在使用旧版本,解决的方法就是每个文件都带一个相应内容的hash值作为文件名。这样,即使文件缓存了,也可以得到更新。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。