为什么要使用 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的属性) - 场景:
- 浏览器向服务器请求了一个
a.js
。- 服务器说:‘你烦不烦? 我们约定个时间
Expires
,时间还没到就别来烦我了’。- 然后服务器就返回了
a.js
和过期时间Expires
。- 后续浏览器的请求 会先比对当前时间是否以及大于了Expires过去时间。
如果还没过期 就不会发起请求 使用缓存
,如果过期了 就会重新发起请求
。
- 缺点:
可能时间过期了,但是重新发起请求后 发现当前资源a.js
并没有发现变化。这样就造成了请求的浪费
- 进阶:
使用Last-Modified
和If-Modified-Since
使服务器和浏览器之间在Expires
的基础上,增加一个群文件最新修改时间
来辅助判断是否应该使用缓存
Last-Modified / if-Modified-Since
- Last-Modified:响应头 资源最新修改时间
由服务器告诉浏览器
- if-Modified-Since:请求头 资源最新修改时间
由浏览器告诉服务器
。和Last-Modified是成对出现的
,它们两个会共同对比来决定
这个文件要不要重新发送 - 场景:
- 浏览器向服务器请求了一个
a.js
。- 服务器说:’你烦不烦? 我们约定个时间
Expires
,另外再给你一个文件最新修改时间Last-Modified
,到时候时间到期了,我们就比对文件最新修改时间,对得上你就继续使用缓存‘。- 然后服务器就返回了
a.js
和过期时间Expires
和文件最新修改时间Last-Modified
。- 后续浏览器请求会先对比是否超过了
Expires
过期时间。没过期就不发起请求,仅使用缓存
。如果过期了 浏览器在后续请求服务器时就会带上文件最新修改时间If-Modified-Since
。- 服务器收到该请求后 会将
Last-Modified 和 if-Modified-Since
进行比对。- 如果
Last-Modified 和 if-Modified-Since
不一样,服务器就会去查找最新的a.js
,同时会再次返回最新的a.js
和Expires
和Last-Modified
。- 如果
Last-Modified 和 if-Modified-Since
一样。服务器就会返回304 - Not-Modified
,表示之前的缓存还可以继续使用。
- 缺点:
Expires
不太稳定,浏览器端可以随意的修改Expires
。Last-Modified
只能精确到秒。在极端情况下,假设文件在1s内发生了变动,那么此时Last-Modified
就无法感知到该文件的变化,这样浏览器永远都拿不到最新的文件资源。
- 进阶:
让服务器和浏览器在过期时间Expiress
+ 最新修改时间Last-Modified
的基础上,增加一个文件内容唯一对比标记Etag
和if-None-Match
。而Expires
不太稳定 再加入一个max-age
来加以代替。
Etag / if-None-Match
- Etag:响应头,资源标识,由服务器告诉浏览器
- if-None-Match:请求头,资源缓存标识,由浏览器告诉服务器,其实就是
上次服务器给浏览器的 Etag
。和 Etag 是一对的,它们会进行对比(用法比 if-Modified-Since 更高级一些) - 场景:
- 浏览器向服务器发起了
a.js
的请求- 服务器说:'你烦不烦? 我们约定个时间
Expires
,再给你一个max-age=60(秒)
,Last-Modified
也给你,另外再给你一个文件内容唯一标识符Etag
'。- 然后服务器就返回了
a.js
和过期时间Expires
和max-age=60
,和文件最新修改时间Last-Modified
和文件内容唯一标识符Etag
。- 后续浏览器在
max-age=60秒
内,就不会再次发起新的请求而是直接使用缓存。并且此时因为有了max-age
的存在,Expires
已经没用了。- 后续浏览器请求在
max-age=60秒
后,会携带上If-Modified-Since
(服务器发送的Last-Modified
) 和if-None-Match
(服务器发送的Etag
)。- 服务端会比对
if-None-Match
和Etag
,尽管此时也传递了If-Modified-Since
,但是服务端不会再对比if-Modified-Since
和Last-Modified
。因为Etag
的优先级大于Last-Modified
。Etag
更精准的解决了文件资源在1s内的变动问题
。7.服务端比对后发现,
if-None-Match 和 Etag
不想等。说明a.js
被修改过,服务器就会返回最新的a.js
和全新的Etag
和max-age
,也会同时返回Expires
和Last-modified
,虽然它俩已经没什么作用了。
- 服务端经对比发现,
if-None-Match
与Etag
相等。说明a.js
没有任何变化,返回状态码304
告诉浏览器继续使用之前的本地缓存
- 缺点:
在Expires
和max-age
都没有过期的情况下,浏览器是没有办法主动知道文件资源是否变动。因为在时间未到的情况下,浏览器肯定使用本地的缓存资源。
- 进阶:
这种问题在HTTP协议本身上来讲,就很难解决了
- 通过
md5 / hash 缓存
- 通过
通过不缓存 html,为静态文件添加 md5 或者 hash 标识,来解决浏览器无法跳过缓存过期时间内主动感知文件变化的问题。
只需在项目每次发布迭代的时候,给静态文件添加不同的 md5/hash 标识即可。因为文件名并不一样,服务端会认为是新的文件,所有跟各种缓存字段都没有任何关系,也就不会存在缓存问题。
- 通过
CDN 缓存
- 通过
CDN 是构建在网络之上的内容分发网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡,内容分发,调度等功能模块,使用户就近获取所需内容,降低网络阻塞,提高用户访问响应速度和命中率,有一个字段是专门给 CDN 来使用的s-maxage=x(单位秒)
CDN 缓存的工作方式
- 第一次请求
- 浏览器向服务器请求
a.js
资源。- 服务端:'
a.js
这个文件我给我小弟 CDN 了,以后你要这个就找 CDN 吧,别找我了。- 成功返回
a.js
给 CDN,CDN 进行缓存
。同时CDN 返回给浏览器
,浏览器自己也进行了缓存
'。
- 后续请求
- 浏览器缓存时间过期,再次发起请求时。这时候 服务器就不会理你了。此时
CDN
会帮你查找该资源,同时也分几种情况:1-1. CDN节点自己缓存的文件还没有过期,CDN会打回该请求。返回状态吗
304
,告诉浏览器 之前的缓存资源还能使用。
1-2. CDN节点自己缓存的文件已经过期了。为了保险起见,CDN自己会发生请求到源服务器,成功拿回最新数据后,再返回给浏览器。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。