浏览器缓存机制
浏览器缓存机制,其实主要就是 HTTP协议定义的缓存机制。
但是也有非 HTTP 协议定义的缓存机制,例如:HTML中的 mata 标签定义的缓存:<meta http-equive='Pragma' content='no-catche'> 这个代码告诉浏览器上述页面不缓存,这样页面每次访问都会从新去服务器拉取。使用很简单,但是只有(IE)支持,并且代理服务器不支持这种缓存,因为代理服务器不会读取解析 HTML 内容本身。这个仅对页面有效,对页面的资源无效。 <meta http-equive='expires' content='mon, 18 apr 2016 14:30:00 GMT'> 也是仅有IE支持。 优先级: Pragma > Cache-Control > Expires
- 浏览器缓存是如何工作的?
浏览器发出的所有 HTTP 请求会首先被路由到浏览器的缓存,以查看是否缓存了可以用于实现请求的有效响应。核心就是浏览器把请求过来的结果保存在了本地、或者内存中。而不用每次都想服务器发送相同的请求。如果多次打开相同的页面,第一次访问这个页面的时候,浏览器将下载下来的js、css、图片 保存在了本地,之后的每次请求,这些需要下载的js、css、图片 都从本地读取,那么效率就快了很多。但是如果每次都从浏览器读取缓存,如果数据、文件发生了变化,那么不就展示了错误的、过时的信息了嘛。因此:- 强缓存:服务端和浏览器约定一个有效期,请求发起之前,判断:如果有效期过了,那么就不走缓存,从新从服务器请求新的数据,如果还在有效期之内,那么就走缓存,不请求接口了。
-
协商缓存:浏览器向服务器发起一个新的请求,有可能存在这个请求已经超出了有效期,但是这个文件并没有发生变更,其实还是可以使用缓存数据的。如何判断文件是否有更新呢?两张方式:
- 在上一次服务端告诉客户端约定的有效期的同时,同时响应头中(Response-Headers)告诉服务端这个文件的最后更新时间(Last-Modified)。当重新请求试图再次下载这个文件的时候,请求头(Request-Headers)中带上这个最后更新时间(If-Modified-Since),服务器检查这个更新时间和服务端这个文件的最后更新时间是否一致,不一致,则更新,重新请求,下载正确的数据,相应头中返回 Last-Modified, 更新Last-Modified;如果一致表示没有更新,返回304,客户端可以使用读取缓存。
- 在上一次服务端告诉客户端约定的有效期的同时,同时响应头中(Response-Headers)告诉服务端这个文件的版本号 (ETag),当服务端更新该文件的时候,重新生成一个独一无二的版本号。再次发送请求的时候,请求头(Request-Headers) 中带上这个文件的版本号(If-None-Match: '212143565764343') check 一下版本号是否一致,如果一致,接口返回304,读取缓存就行了,如果不一致,下载正确的数据,同时相应头中返回 ETag,跟新本地这个文件的 ETag
强缓存
Expires: Wed, 29 July 2021 08:43:55 GMT。 表示缓存到期时间。
Expires 的值是一个时间点,表示这个缓存的到期时间。这是HTTP 1.0 的产物。时间(UTC)是由服务器发送的可能存在误差,如果客户端与服务器时间存在差异,可能会出现问题。存在版本问题,如果文件到期之前修改了,客户端不可知。Cache-Control: public, max-age=2552; 这是HTTP 1.1 的产物。存在版本问题,如果文件到期之前修改了,客户端不可知。。Cache-Control的参数可以设置很多值,也可以组合使用,逗号隔开。譬如
### 在请求中使用Cache-Control 时,它可选的值有:
- no-cache 告知代理服务器不直接使用缓存,要向源服务器发起请求。
- no-store 用于防止重要的信息被无意的发布。在请求消息中发送将使得请求和响应消息都不会被缓存到内存或者 internet 临时文件中。**优先级最高**
- max-age=2552 告知服务器客户端希望获取一个在小于 2552 s 内更新过的资源。
- max-stale[=delta-seconds] 告知(代理)服务器客户端愿意使用一个超出缓存时间的资源。如果有值,那么超出缓存时间为 delta-seconds ,没有,则为任意超出时间。
- min-fresh 指示客户机可以接收超出 响应时间小于当前时间加上指定时间的响应。
- no-transform 告知(代理)服务器客户端希望获取一个没有被转换过(比如:压缩)的资源。
- only-if-cached 告知(代理)服务器客户端希望获取一个缓存内容(如果有的话),而不用向源服务器请求。
- cache-extension 自定义扩展值,如果服务器不识别,则忽略。
### 在响应中使用Cache-Control 时,它可选的值有:
- public 任何情况下该资源都将被缓存。(即使是需要HTTP认证的资源)
- Private[='filed-name'] 指示对于返回报文整个或部分响应(若指定了 filed-name 则为 filed-name的字段数据)仅开放给部分用户(服务器指定的 share-name 如代理服务器)做缓存使用,不能被共享缓存处理。这个相应消息对于其他用户的请求无效。服务端不用缓存,只有客户端使用缓存。(cache-control 的默认取值)
- max-age=2552 告知客户端该资源在 2552s 内是新鲜的,而不用想服务端发起请求。
- s-max-age=delta-seconds 同max-age,但仅应用于 共享缓存(如代理)。
- no-cache 指示请求或者响应消息不直接使用缓存,但是是否使用缓存需要经过协商缓存来验证决定。
- no-store 所有的内容都不会被保存到缓存或者 internet 临时文件中。
- only-if-cached 告知(代理)服务器客户端希望获取一个缓存内容(如果有的话),而不用向源服务器请求、
- must-revalidate 当前资源一定是向源服务发起请求验证的,而不是代理服务器上的缓存。若请求失败,则会返回504。
- proxt-revalidate 和must-revalidate 一样,但是仅能应用于共享缓存(代理服务器)。
- cache-extension 自定义扩展值,如果服务器不识别则忽略。
**如果Expire 和 Cache-Control中的max-age 同时存在,那么优先考虑max-age。**
协商缓存:要配合Cache-Control使用。
Last-Modified / If-Modified-Since | If-Umnmodified-Since(最后更新时间是否不一致)
ETag / If-None-Match | If-Match(ETag是否一致) 实际上ETag并不是文件的版本号,而是一串可以代表该文件唯一的字符串。
**如果ETag 和 Last-Modified 同时存在,那么优先考虑ETag。**
HTTP 1.1 出现的 ETag的出现主要是为了解决几个 Last-Modified的难以解决的问题:
1. Last-Modified 的最小单位精确到 秒,如果文件在一秒钟之内改变了多次,那么它将不能准确标注文件的修改时间
2. 如果某一个文件是注定会被定期生成的,即使它的内容没有发生改变,那么它的 Last-Modified 也会发生改变,导致没法使用缓存
3. 有可能存在服务器没有准确获取文件修改时间的问题,也或者与代理服务器时间不一致的情形。
ETag 也不是完美无缺的:
1. 计算ETag 需要性能损耗。
2. 如果是分布式服务器存储的时候,计算ETag的算法不一致,会导致ETag不一致。从一台服务器上请求过来的资源去另外一台服务器上进行文件校验的时候ETag不一致。
不能使用缓存的情况:
- post 请求 无法被缓存
- HTTP信息头中包含:Cache-Control: no-cache; Pragma: no-cache(HTTP 1.0) 或者 Cache-Control: max-age=0;等告诉浏览器不用缓存。
- HTTP 响应头中不包含:Etag\Last-Modified 也不包含:Expires\Cache-Control 的请求无法被缓存。
- 需要根据 Cookie,认证信息等决定输入内容的动态请求是不能被缓存的。
- 经过 HTTPS 安全加密的请求。(也有人经过测试发现,IE其实在头部加上Cache-Control: max-age信息,Firefox在 Cache-Control: public之后能够对HTTPS的资源进行缓存,参考《HTTPS的七个误解》http://www.ruanyifeng.com/blog/2011/02/seven_myths_about_https.html)
用户行为与浏览器缓存
1. 地址栏回车 强缓存(Expires/Cache-Control):有效 协商缓存(Last-Modified/ETag):有效
2. 页面链接跳转 强缓存(Expires/Cache-Control):有效 协商缓存(Last-Modified/ETag):有效
3. 新开窗口 强缓存(Expires/Cache-Control):有效 协商缓存(Last-Modified/ETag):有效
4. 浏览器的前进、后退 强缓存(Expires/Cache-Control):有效 协商缓存(Last-Modified/ETag):有效
5. F5 刷新 强缓存(Expires/Cache-Control):失效 协商缓存(Last-Modified/ETag):有效
6. ctrl + F5 强制刷新 强缓存(Expires/Cache-Control):失效 协商缓存(Last-Modified/ETag):失效
废弃和更新已缓存的策略:
如果资源已经被修改,但是这个资源被缓存了,客户端因为 Expires \ max-age 无法看到最新的修改。如何解决? 无法解决,这是一个伪命题,我们只能通过改变资源文件的请求网址。 给资源文件名加上 指纹码(或者版本号)
定义缓存策略的方法和技巧:
1. **使用一致的网址**:如果在不同的网址中提供相同的内容,将会多次获取和存储该内容。(网址区分大小写)
2. **确保服务器提供验证令牌ETag**。通过验证令牌,如果服务器上的资源未被更改,就不必传输相同的字节。
3. **确定中继缓存可以缓存哪些资源**。对所有用户完全相同的请求资源很适合由CDN或其他中继缓存进行缓存。
4. **确定每个资源的最有缓存周期**。不同的资源可能有不同的更新要求,审查并确定每个资源的max-age。
5. **确定网站的最佳缓存层级**。对HTML文档使用组合使用包含指纹码的资源网址,短时间、no-cache的生命周期,可以控制客户端获取更新的速度。
6. **搅动最小化**。有些资源的更新可能会比其他资源更新更频繁。如果资源的饿特定部分(例如:javascript 函数、css样式)会经常更新,应该考虑将其代码作为单独的文件提供。这样每次获取更新时,剩余内容(不会频繁更新的代码)可以中缓存中获取,确保下载的内容量最少。