参考以下文章
https://segmentfault.com/a/1190000006741200
https://github.com/zuopf769/notebook/tree/master/fe/%E5%89%8D%E7%AB%AF%E5%BF%85%E9%A1%BB%E8%A6%81%E6%87%82%E7%9A%84%E6%B5%8F%E8%A7%88%E5%99%A8%E7%BC%93%E5%AD%98%E6%9C%BA%E5%88%B6
https://segmentfault.com/a/1190000015809379
注意,我们讨论的所有关于缓存资源的问题,都仅仅针对GET请求。而对于POST, DELETE, PUT这类行为性操作通常不做任何缓存。
浏览器缓存==客户端缓存
下面这张图展示了某一网站,对不同资源的请求结果,其中可以看到有的资源直接从缓存中读取,有的资源跟服务器进行了再验证,有的资源重新从服务器端获取。
浏览器缓存分为两种,分别是强制缓存,协商缓存
强制缓存
浏览器在加载资源时,会根据这个资源的http请求头去看是否命中强缓存,如果命中强缓存就直接用浏览器缓存的资源,这个过程中没有发请求给服务器
协商缓存
这个过程有发请求给服务器,服务器返回304,不会返回数据,还是继续用浏览器缓存的数据。
与强制缓存有关的两个属性
Cache-Control和Expires
- Cache-Control是HTTP1.1中新增的响应头
- Expires是HTTP1.0中的响应头
- Cache-Control使用的是相对时间
- Expires指定的是具体的过期日期而不是秒数。因为很多服务器跟客户端存在时钟不一致的情况,所以最好还是使用Cache-Control.
- Cache-Control和Expires同时使用的话,Cache-Control会覆盖Expires
Cache-Control有什么属性
- max-age(单位为s)
指定设置缓存最大的有效时间,定义的是时间长短。当浏览器向服务器发送请求后,在max-age这段时间里浏览器就不会再向服务器发送请求了。
- public
指定响应可以在代理缓存中被缓存,于是可以被多用户共享。如果没有明确指定private,则默认为public
- private
响应只能在私有缓存中被缓存,不能放在代理缓存上。对一些用户信息敏感的资源,通常需要设置为private。
- no-cache
表示必须先与服务器确认资源是否被更改过(依靠If-None-Match和Etag),然后再决定是否使用本地缓存。
- no-store
绝对禁止缓存任何资源,也就是说每次用户请求资源时,都会向服务器发送一个请求,每次都会下载完整的资源。通常用于机密性资源。
HTTP通过缓存将服务器资源的副本保留一段时间,这段时间称为新鲜度限值。这在一段时间内请求相同资源不会再通过服务器,也就是不会向服务器发起请求。HTTP协议中Cache-Control 和 Expires可以用来设置新鲜度的限值,前者是HTTP1.1中新增的响应头,后者是HTTP1.0中的响应头。二者所做的事时都是相同的,但由于Cache-Control使用的是相对时间,而Expires可能存在客户端与服务器端时间不一样的问题,所以我们更倾向于选择Cache-Control。
当expire和cache-control都过期了,也就是过了这个新鲜度限值了,我们这个时候就要用到协商缓存了,这个时候就要跟服务器进行验证了。
- 如果资源发生变化,则需要取得新的资源,并在缓存中替换旧资源。
- 如果资源没有发生变化,缓存只需要获取新的响应头,和一个新的过期时间,对缓存中的资源过期时间进行更新即可。
HTTP1.1推荐使用的验证方式是If-None-Match/Etag,在HTTP1.0中则使用If-Modified-Since/Last-Modified。
与协商缓存有关的两个属性
If-None-Match/Etag或者If-Modified-Since/Last-Modified
-
Etag是HTTP1.1推荐使用的验证方式,是指根据实体内容生成一段hash字符串,标识资源的状态,由服务端产生。浏览器会将这串字符串传回服务器,验证资源是否已经修改,如果没有修改,过程如下:
- ETag值通常由服务器端计算,并在响应客户端请求时将它返回给客户端
- If-Modified-Since与Last-Modified验证过程与etag类似。
这两个方式来验证时会存在下面问题:
有些文档资源周期性的被重写,但实际内容没有改变。此时文件元数据中会显示文件最近的修改日期与If-Modified-Since不相同,导致不必要的响应。
有些文档资源被修改了,但修改内容并不重要,不需要所有的缓存都更新(比如代码注释)
最佳优化策略(消灭304)
因为协商缓存本身也有http请求的消耗,所以最佳优化策略就是要尽可能的将静态文件存储为较长的时间,多利用强制缓存而不是协商缓存,这就是消灭304
但是给文件设置一个很长的cache-control也会带来很多其他的问题,最主要的问题是静态内容个更新时,用户不能及时获得更新的内容,这个时候就需要利用hash来对文件进行命名,通过每次更新不同的静态文件名来消除强制缓存的影响。(hash值变了,就相当于要请求一个新的文件,那么肯定就跳过协商缓存和强制缓存,直接请求新的文件)
hash命名:
http://xxx.com/main.5eas34fa.js
http://xxx.com/main.js?5eas34fa
http://xxx.com/5eas34fa/main.js
总结
- 浏览器端缓存分为200 from cache和304 not modified
- HTTP协议中Cache-Control 和 Expires可以用来设置新鲜度的限值,前者是HTTP1.1中新增的响应头,后者是HTTP1.0中的响应头。
- max-age(单位为s)而Expires指定的是具体的过期日期而不是秒数
- Cache-Control和Expires同时使用的话,Cache-Control会覆盖Expires
客户端不用关心ETag值如何产生,只要服务在资源状态发生变更的情况下将ETag值发送给它就行 - Apache默认通过FileEtag中FileEtag INode Mtime Size的配置自动生成ETag(当然也可以通过用户自定义的方式)。
- ETag常与If-None-Match或者If-Match一起,由客户端通过HTTP头信息(包括ETag值)发送给服务端处理。
- Last-Modified常与If-Modified-Since一起由客户端将Last-Modified值包括在HTTP头信息中发给服务端进行处理。
- 有些文档资源周期性的被重写,但实际内容没有改变。此时文件元数据中会显示文件最近的修改日期与If-Modified-Since不相同,导致不必要的响应。
补充说明:
web缓存分为两种:
- http缓存(从第二次请求开始的,也就是我们上面所说的强制缓存和协商缓存,都是根据response header中的一些属性来决定的。)
- 浏览器缓存
2.1本地存储小容量
Cookie主要用于用户信息的存储,Cookie的内容可以自动在请求的时候被传递给服务器。
LocalStorage的数据将一直保存在浏览器内,直到用户清除浏览器缓存数据为止。
SessionStorage的其他属性同LocalStorage,只不过它的生命周期同标签页的生命周期,当标签页被关闭时,SessionStorage也会被清除。
2.2本地存储大缓存
- IndexDB
-
WebSql
2.3 应用缓存和PWA