缓存
这个机制你说他好,确实好,能节省加载时间,节省流量。但也有很差劲的地方,假如线上有个BUG,开发修改了js代码,但用户浏览的资源死活就是上一份缓存...不刷新缓存,严重点搞不好开发要被祭天!
- 当用户访问网页时,我们会得到来自服务器发送过来的一些资源,如js、img、css、html等,得到资源后这些资源可能就会被缓存到本地磁盘上。当用户二次请求的时候如果这些资源有缓存,那么此时浏览器就会直接从缓存中把资源拿出来。
- 缓存有协商缓存和强缓存
状态码304是什么
- 未修改
- 状态码304代表未修改(Not Modified)。 在HTTP协议中,当客户端发出一个资源的请求,而这个资源已经在客户端或其代理服务器中被缓存,并且在最近一次请求之后没有任何修改发生时,服务器会返回304状态码给客户端。这表明服务器认为资源未被修改,因此客户端可以继续使用本地缓存中的资源,而不是重新向服务器请求数据。这样可以节省网络带宽和服务器资源的开销。
- 需要注意的是,虽然304状态码与重定向无关 ,但它通常与条件请求一起使用,如
If-Modified-Since
或If-None-Match
头字段。在这种情况下,服务器会根据请求头中的条件来判断资源是否需要更新。如果条件满足,服务器可能会返回一个不同的状态码,如302或307,以指示资源的最新位置或进行其他类型的操作。
协商缓存中ETag与Last-Modified
这两个服务器响应头使用用于协商缓存中的
- Last-modified表示本地文件最后修改时间,由服务器返回。精确到秒级,last-Modified 标识即使文件内容相同,但是只要修改时间发生变化,都会再次返回该资源,而 ETag 可以判断出文件内容是否相同,相同则直接返回304,缓存策略更佳。
如果使用的是 Last-Modified ,比如文件A加上了一段文字并保存,然后又把这段文字删除后再保存。此时尽管资源的内容不变,但是资源的修改时间发生了变化,服务端依然会返回该资源,而不是返回304。
如果使用的是 ETag 字符串,那么文件A的内容其实是没有发生变化的,浏览器依然会用本地的缓存。
- if-modified-since是浏览器在请求数据时返回的,值是上次浏览器返回的Last-modified
- ETag是一个文件的唯一标识符,当资源发生变化时这个ETag就会发生变化。弥补了上面last-modified可能出现文件内容没有变化但是last-modified发生了变化出现重新向服务器请求资源情况。这个值也是服务器返回的
- if-none-match是浏览器请求数据时带上的字段,值是上次服务器返回的ETag
强缓存中的Expires与cache-control
- 在浏览器第一个请求资源时,服务器端的响应头会附上Expires这个响应字段,当浏览器在下一次请求这个资源时会根据上次的expires字段是否使用缓存资源(当请求时间小于服务端返回的到期时间,直接使用缓存数据)
expires是根据本地时间来判断的,假设客户端和服务器时间不同,会导致缓存命中误差
- 那Expires缺点很明确,是根据本地时间来判断是否命中缓存的,但一般情况下用户本地的时间存在着不确定性,那新的http1.1规范中就有了cache-control这么个东西,并且这玩意优先级还是大于Expires.
- 以下就是cache-control中的值,当然这部分抄来的,因为我基本上只会接触到max-age、private、no-store、no-cache
属性值 | 值 | 备注 | |
---|---|---|---|
max-age | 3600 | 例如值为3600,表示(当前时间+3600秒)内不与服务器请求新的数据资源 | |
s-maxage | 和max-age一样,但这个是设定代理服务器的缓存时间 | ||
private | 内容只缓存到私有缓存中(仅客户端可以缓存,代理服务器不可缓存) | ||
public | 所有内容都将被缓存(客户端和代理服务器都可缓存) | ||
no-store | 不缓存任何数据 | ||
no-cache | 储存在本地缓存区中,只是在与原始服务器进行新鲜度再验证之前,缓存不能将其提供给客户端使用 |
no-store与no-cache
-
no-cache 和 no-store 都是 HTTP 缓存控制指令,它们在管理网页资源的缓存行为时有所不同
-
no-cache:
- no-cache 指令并不意味着完全禁止缓存。实际上,它允许缓存存储响应,但要求在使用该缓存响应之前,必须先向原始服务器验证其有效性。
- 当一个请求带有 no-cache 指令时,浏览器会发送一个条件请求(如使用 If-Modified-Since 或 If-None-Match 头部),询问服务器缓存的内容是否仍然有效。如果服务器确认内容没有变化,就会返回一个 304 (未修改)状态码,指示浏览器可以继续使用缓存的版本;如果内容有变化,服务器则会返回新的内容。
-
no-store
- no-store 指令则是彻底禁止缓存。它要求浏览器和任何中间代理都不应存储任何关于这个请求或响应的信息。
- 当一个响应标有 no-store 指令时,浏览器和代理必须立即删除任何已存储的对应缓存项,并且不得在本地存储请求或响应的任何部分。这意味着每次请求都必须直接发送到服务器,即使网络条件良好,也无法使用缓存的响应。
强缓存与协商缓存
-
假设设置了Etag与last-modified,用户请求资源依然来自缓存。在控制器中发现响应为
200 (form disk cache)
或者其他的。这里以chrom浏览器来简单说明- 有弱缓存的时候会默认生效强缓存。即当有弱缓存如有etag,那强缓存
Cache-Control
自动生效,默认值是private。 - 有协商缓存时,如果你要启用
304 Not Modified
也就是304缓存,你需要将响应头中的Cache-Control
设置为no-cache
。在ngixn中add_header Cache-Control "no-cache";
- 有协商缓存的时候,如果说你要得到的是
200 OK
而不是200 (form disk cache)
或者其他的,人话就是我不需要缓存,你需要设置cache-control值为no-store - 那么相对应的,如果我没有协商缓存的时候,那强缓存也是没有的。每次都返回的是200 OK!
- 无协商缓存缓存,把Cache-control设置为private或者no-store或者no-cache或者max-age=0,每次返回的是200
- 有弱缓存的时候会默认生效强缓存。即当有弱缓存如有etag,那强缓存
注:有协商缓存的时候,强级存
cache-control
的默认值private
会自动生发,但其有效性存在不确定性,有时半小时内失效,有时迟迟不失效,建议手动设置为'no-cache',有效期的不确定性,会导致一个们题,那就是我明明己经发布了最年代码到服务器,但本地的就是不失效,导致用户一直访问老页面。普通刷新可以去除首一个的强缓存,如前后端分离中的对index.html的这一条网络请求!但,这是无法去除iframe中的页面强缓存(关于这一点是否存在其他的情况还尚未可知)。
强缓存失效
- 前面提到过cache-control是有个max-age的那么等到这个时间失效强缓存也就无效了
- 触发浏览器强制刷新行为,这个我本地也试过,不知道为什么在比如360安全浏览器上似乎有时候是无效的!当然这有可能触发的是强制刷新首一条请求的请求,导致有些js无法正常加载最新的,导致缓存无法去除等!iframe指向的子页面资源缓存也会呗清除
- 给url加时间戳,这样也可以让服务器认为是新的资源请求!
强刷与iframe缓存问题
- 假如ifram是通过js渲染的,就比如现在的前后端分离,同步渲染时强刷课去掉非html页面强缓存,但异步渲染时所有的静态资源可能依旧存在
- 若iframe指向的页面是html直接渲染的,强刷是可行的,是可以去掉静态资源的强缓存。
如何有效去除缓存
- 那,前面讲到很多关于缓存的请求头,那我们直接把用于缓存的请求头全部去掉就好了,这里以前后端分离为例子,我们可以将index.html入口配置响应头,将
Etag
和last-modify
取消设置,这样强缓存与协商缓存都不存在,只要index.html缓存失效,那么入口是新的,加载的js映射文件也会是新的。 - 我们可以单独设置htlm的
cache-control:no-cache
,触发304 not modify
,让强缓存失效,每次都是进行协商缓存,当然这就需要等待上次的协商缓存失效。不过很奇怪,有的时候,协商缓存就是不更新,烦的很,具体原因我也不是特别清楚! - 我们可以单独设置htlm的
cache-control:no-stroe
,是指返回200 - 给url加时间戳,让服务器知道咱才是爸爸~!