2
为什么要使用 HTTP 缓存
  • 对于资源:

假如每次请求的请求头响应头分别都是1k,请求文件大小是10k。那么一次请求就是12k大小,n次就是(12 * n)k

  • 对于前端:

前端每次请求完毕都要重新渲染 影响用户体验

  • 对于后端:

没有缓存机制的话前端会频繁的请求后端的接口。后端每次都要提供查找和下载等功能。如果请求基数比较大,服务器就会存在较大的压力
为了减少网络带宽消耗、减少延迟与网络阻塞,同时降低服务器压力提高服务器性能

HTTP 缓存的内容是什么
  • CSS JS 图片等 更新频率不大的静态文件等
HTTP 缓存头部字段
  • Cache-Control
存在于请求头和响应头中,是缓存控制字段
控制HTTP缓存的最高指令,缓存不缓存 它说了算
  • 相关value
no-store: 不缓存
no-cache: 缓存 但是前端使用缓存前 都会请求服务器 来判断当前的缓存资源是否是最新 只是用不过期的缓存
max-age=x(单位秒):请求缓存后的x秒内 不再发起请求,HTTP1.1以上
s-maxage=x(单位秒):代理服务器请求源站后x秒内不再发起请求,只对CDN有效
public:客户端和代理服务器(CDN)都可以缓存
private:只有客户端可以缓存
Expires
  • 响应头 代表资源过期时间,由服务器返回提供。但是浏览器端可以修改Expires。它是HTTP1.0的属性,在与max-age共存的情况下,max-age的优先级较高(因为max-age是HTTP1.1的属性)
  • 场景:
  1. 浏览器向服务器请求了一个a.js
  2. 服务器说:‘你烦不烦? 我们约定个时间Expires,时间还没到就别来烦我了’。
  3. 然后服务器就返回了a.js和过期时间Expires
  4. 后续浏览器的请求 会先比对当前时间是否以及大于了Expires过去时间。如果还没过期 就不会发起请求 使用缓存如果过期了 就会重新发起请求
  • 缺点:
可能时间过期了,但是重新发起请求后 发现当前资源a.js并没有发现变化。这样就造成了请求的浪费
  • 进阶:
使用Last-ModifiedIf-Modified-Since
使服务器和浏览器之间在Expires的基础上,增加一个群文件最新修改时间来辅助判断是否应该使用缓存
Last-Modified / if-Modified-Since
  • Last-Modified:响应头 资源最新修改时间 由服务器告诉浏览器
  • if-Modified-Since:请求头 资源最新修改时间 由浏览器告诉服务器。和Last-Modified是成对出现的,它们两个会共同对比来决定这个文件要不要重新发送
  • 场景:
  1. 浏览器向服务器请求了一个a.js
  2. 服务器说:’你烦不烦? 我们约定个时间Expires,另外再给你一个文件最新修改时间Last-Modified,到时候时间到期了,我们就比对文件最新修改时间,对得上你就继续使用缓存‘。
  3. 然后服务器就返回了a.js和过期时间Expires和文件最新修改时间Last-Modified
  4. 后续浏览器请求会先对比是否超过了Expires过期时间。没过期就不发起请求,仅使用缓存。如果过期了 浏览器在后续请求服务器时就会带上文件最新修改时间If-Modified-Since
  5. 服务器收到该请求后 会将Last-Modified 和 if-Modified-Since进行比对。
  6. 如果Last-Modified 和 if-Modified-Since不一样,服务器就会去查找最新的a.js,同时会再次返回最新的a.jsExpiresLast-Modified
  7. 如果Last-Modified 和 if-Modified-Since一样。服务器就会返回304 - Not-Modified,表示之前的缓存还可以继续使用。
  • 缺点:
Expires不太稳定,浏览器端可以随意的修改Expires
Last-Modified只能精确到秒。在极端情况下,假设文件在1s内发生了变动,那么此时 Last-Modified 就无法感知到该文件的变化,这样浏览器永远都拿不到最新的文件资源。
  • 进阶:
让服务器和浏览器在过期时间Expiress + 最新修改时间Last-Modified 的基础上,增加一个文件内容唯一对比标记Etagif-None-Match。而Expires不太稳定 再加入一个max-age来加以代替。
Etag / if-None-Match
  • Etag:响应头,资源标识,由服务器告诉浏览器
  • if-None-Match:请求头,资源缓存标识,由浏览器告诉服务器,其实就是上次服务器给浏览器的 Etag。和 Etag 是一对的,它们会进行对比(用法比 if-Modified-Since 更高级一些)
  • 场景:
  1. 浏览器向服务器发起了a.js的请求
  2. 服务器说:'你烦不烦? 我们约定个时间Expires,再给你一个max-age=60(秒)Last-Modified也给你,另外再给你一个文件内容唯一标识符Etag'。
  3. 然后服务器就返回了a.js和过期时间Expiresmax-age=60,和文件最新修改时间Last-Modified和文件内容唯一标识符Etag
  4. 后续浏览器在max-age=60秒内,就不会再次发起新的请求而是直接使用缓存。并且此时因为有了max-age的存在,Expires已经没用了。
  5. 后续浏览器请求在max-age=60秒后,会携带上If-Modified-Since(服务器发送的Last-Modified) 和 if-None-Match(服务器发送的Etag)。
  6. 服务端会比对if-None-MatchEtag,尽管此时也传递了If-Modified-Since,但是服务端不会再对比if-Modified-SinceLast-Modified。因为Etag的优先级大于Last-ModifiedEtag更精准的解决了文件资源在1s内的变动问题

7.服务端比对后发现,if-None-Match 和 Etag不想等。说明a.js被修改过,服务器就会返回最新的a.js和全新的Etagmax-age,也会同时返回ExpiresLast-modified,虽然它俩已经没什么作用了。

  1. 服务端经对比发现,if-None-MatchEtag相等。说明a.js没有任何变化,返回状态码304告诉浏览器 继续使用之前的本地缓存
  • 缺点:
Expiresmax-age都没有过期的情况下,浏览器是没有办法主动知道文件资源是否变动。因为在时间未到的情况下,浏览器肯定使用本地的缓存资源。
  • 进阶:
这种问题在HTTP协议本身上来讲,就很难解决了
    1. 通过 md5 / hash 缓存
通过不缓存 html,为静态文件添加 md5 或者 hash 标识,来解决浏览器无法跳过缓存过期时间内主动感知文件变化的问题。
只需在项目每次发布迭代的时候,给静态文件添加不同的 md5/hash 标识即可。因为文件名并不一样,服务端会认为是新的文件,所有跟各种缓存字段都没有任何关系,也就不会存在缓存问题。
    1. 通过 CDN 缓存
CDN 是构建在网络之上的内容分发网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡,内容分发,调度等功能模块,使用户就近获取所需内容,降低网络阻塞,提高用户访问响应速度和命中率,有一个字段是专门给 CDN 来使用的s-maxage=x(单位秒)
CDN 缓存的工作方式
  • 第一次请求
  1. 浏览器向服务器请求a.js资源。
  2. 服务端:'a.js这个文件我给我小弟 CDN 了,以后你要这个就找 CDN 吧,别找我了。
  3. 成功返回a.js给 CDN,CDN 进行缓存。同时 CDN 返回给浏览器浏览器自己也进行了缓存'。
  • 后续请求
  1. 浏览器缓存时间过期,再次发起请求时。这时候 服务器就不会理你了。此时CDN会帮你查找该资源,同时也分几种情况:

1-1. CDN节点自己缓存的文件还没有过期,CDN会打回该请求。返回状态吗304,告诉浏览器 之前的缓存资源还能使用。
1-2. CDN节点自己缓存的文件已经过期了。为了保险起见,CDN自己会发生请求到源服务器,成功拿回最新数据后,再返回给浏览器。


Funky_Tiger
443 声望33 粉丝

刷题,交流,offer,内推,涨薪,相亲,前端资源共享...