使用 Expires 的缺点:
-
缓存过期以后,服务器不管文件有没有变化,都会再次请求服务器。
-
缓存过期时间是一个具体的时间,这个时间依赖于客户端的时间,如果时间不准确或者被改动,缓存也会随之受到影响。
Cache-Control
请求/响应头,缓存控制字段,精确控制缓存策略。
为了让强缓存更精确,HTTP1.1 增加了 Cache-Control 字段。Cache-Control 既能出现在请求头又能出现在响应头,其不同的值代表不同策略,下面具体分析一下。
Cache-Control 服务端参数:
-
max-age:缓存有效时间,是一个相对时间,比 Expires 表示的时间更精确。
-
s-maxage:用于表示 cache 服务器上(比如 cache CDN,缓存代理服务器)的缓存有效时间,并只对 public 缓存有效。
-
no-cache:不使用本地强缓存,需要使用协商缓存(下面展开)。
-
no-store:直接禁止浏览器缓存数据,每次用户请求该资源,都会向服务器发送请求,每次都会下载完整的资源。
-
public:可以被所有的用户缓存,包括终端用户和中间代理服务器。
-
private:只能被终端用户的浏览器缓存,不允许中间缓存代理进行缓存(默认的)。
Cache-Control 客户端参数:
-
max-stale: 5,表示当客户端到代理服务器上拿缓存时,即使代理缓存过期,但只要过期时间在 5 秒之内,还是可以从代理中获取的。
-
min-fresh: 5,表示代理缓存需要一定的新鲜度,不要等到缓存刚好到期再获取,一定要在到期前 5 秒之前获取,否则将获取不到。
-
only-if-cached:表示客户端只会接受代理缓存,而不会接受源服务器的响应。如果代理缓存无效,则直接返回 504(Gateway Timeout)。
协商缓存
协商缓存主要有四个头字段,它们两两组合一起使用,Last-Modified 和 If-Modified-Since 一组,Etag 和 If-None-Match 一组。当二者同时存在时,Etag 和 If-None-Match 组合的优先级更高。当命中协商缓存的时候,服务器会返回 HTTP 状态码304,让客户端直接从本地缓存里面读取资源。
Last-Modified 和 If-Modified-Since
Last-Modified
响应头,资源最近修改时间,由服务器告诉浏览器。
If-Modified-Since
请求头,资源最近修改时间,由浏览器告诉服务器。其实就是第一次访问服务端时返回的 Last-Modified 的值。
使用场景
当客户端第一次请求服务器的时候,服务端会返回一个 Last-Modified 响应头,该字段是一个标准时间。在客户端请求服务器时会带上 If-Modified-Since 请求头字段,该字段的值就是服务器上一次返回的 Last-Modified 的值。服务器接收到请求后会比较这两个值是否一样,一样就返回 状态码304,让客户端直接从缓存中读取;不一样则返回新文件给客户端并更新 Last-Modified 响应头字段的值。
使用 Last-Modified 和 If-Modified-Since 的优点:
- 当缓存有效时,服务器不会返回文件给客户端,而是直接返回 状态码304,让客户端从缓存中获取文件。大大节省了流量和带宽以及服务器的压力。
使用 Last-Modified 和 If-Modified-Since 的缺点:
- Last-Modified 过期时间只能精确到秒。如果在同一秒内既修改文件又获取文件,客户端将获取不到最新文件。
Etag 和 If-None-Match
Etag
响应头,资源标识,由服务器告诉浏览器。
If-None-Match
请求头,缓存资源标识,由浏览器告诉服务器。其实就是第一次访问服务端时返回的 Etag 的值。
使用场景
为了解决文件修改时间只能精确到秒带来的问题,我们引入 Etag 响应头。Etag 是由文件修改时间与文件大小计算而成,只有当文件内容或修改时间改变时,Etag 的值才会发生变化。
当客户端第一次请求服务器的时候,服务端会返回一个 Etag 响应头。客户端请求服务器的时候会带上 If-None-Match 请求头字段,该字段的值就是服务器上一次返回的 Etag 的值。服务器接收到请求后会比较这两个值是否一样,一样就返回 状态码304,让客户端直接从缓存中读取;不一样就会返回新文件给客户端并更新 Etag 响应头字段的值。
使用 Etag 和 If-None-Match 的优点:
-
当缓存有效时,服务器不会返回文件给客户端,而是直接返回 状态码304,让客户端从缓存中获取文件。大大节省了流量和带宽以及服务器的压力。
-
并且解决了一秒内修改并读取文件的问题。
流程图
扩展
–
缓存失效问题
缓存可以为我们带来好处,大大提升响应速度以及减轻服务端的压力,但是也会出现一些问题,比如我们明明更新了文件版本,为什么客户端获取到的还是旧文件。在不同的时间段有不同的解决方案。
旧方案
旧方案通过人工自己修改文件名或者为文件名带上版本号、时间戳等,这样客户端就会当新文件请求并使用,之前的强缓存就算在有效期内也会失效。
新方案
在现在的构建阶段基本上都不需要人工操作了,都是使用构建工具如 Wbpack、Gulp、Grunt 等构建工具自动完成构建。比如在使用 Webpack 构建的时候,会根据文件名或文件内容自动计算 hash 值来生成新文件标识,当内容或文件名发生改变时,构建出来的文件标识也一定是新的,这样也解决了强缓存还在有效期内文件却已经改变的问题。
pragma
pragma 是旧产物,已经被逐步抛弃,有些网站为了向下兼容还保留了这个字段。pragma 的值为 no-cache 时,表示禁用缓存。优先级是 pragma > Cache-Control > Expires。
缓存存放位置
按缓存存放位置分类,可以分为 memory cache、disk cache、Service Worker 三类,我们可以在 Chrome 的开发者工具中,Network -> Size 一列看到一个请求最终的处理方式:如果是大小 (多少 K, 多少 M 等) 就表示是网络请求,否则会列出 from memory cache、from disk cache、from ServiceWorker,就表示命中了缓存。
- memory cache 是内存中的缓存,(与之相对 disk cache 就是硬盘上的缓存)。按照操作系统的常理:先读内存,再读硬盘。
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!**
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!