缓存,开发绕不开的环节。
web缓存分为很多种,比如数据库缓存、代理服务器缓存、CDN缓存,以及浏览器缓存(localStorage, sessionstorage, cookie)。
一个web应用,需要各式各样的资源(html/css/js, 图片,字体,。。。),浏览器在取这些资源都是通过http请求完成。为了给用户好的体验,需要让这些请求尽可能快和少(减少冗余请求)。HTTP缓存机制就是 针对不同资源类型,采取不同的缓存策略,避免不必要的请求!
常见的 HTTP 缓存只能存储 GET 响应,对于其他类型的响应则无能为力。缓存的关键主要包括request method和目标URI(一般只有GET请求才会被缓存)。
浏览器在每次GET URL时都会先检查URL对应的缓存,除非你指定不使用缓存(强制刷新或者Disable Cache等)
缓存的位置主要是内存和磁盘(本文中用
本地
指代)浏览器会把哪些文件丢进内存中?哪些丢进硬盘中?
关于这点,网上说法不一,不过以下观点比较靠得住:
对于大文件来说,大概率是不存储在内存中的,反之优先
当前系统内存使用率高的话,文件优先存储进硬盘
HTTP 缓存分为以下两种,两者都是通过 HTTP 响应头控制缓存。
- 强制缓存
- 协商缓存
强制缓存
当web应用获取资源时,先从 本地 获取,如果有就直接用,否则,重新发起请求。
返回的状态码为200, 如果是从缓存中获取会有标记(来自内存缓存/来自磁盘缓存)。从内存当中获取的,浏览器关闭后该资源内存会被释放;从磁盘中读取的,关掉浏览器资源依然在。
强制缓存涉及到的头字段有 Expires
(http1.0)和Cache-Control
(http1.1),由服务端设置.
Expires
接受一个 GMT 格式时间
Expires: Wed, 21 Oct 2015 07:28:00 GMT
无效的日期,比如 0,代表着过去的日期,即该资源已经过期。
服务端设置该字段,然后该资源在本地缓存。每次要获取该资源时,就会检查该字段,有效期内直接用否则重新向服务端获取。
注意: 如果在Cache-Control
响应头设置了 “max-age” 或者 “s-max-age” 指令,那么 Expires
头会被忽略。
Cache-Control
此字段拥有强大的缓存控制能力。常见的字段有
- max-age:设置强制缓存的时间,单位s。资源会缓存到本地。
- no-cache:设置不强制缓存,每次都去进行协商缓存,确定资源是否有变更,一般用在index.html。 资源会缓存到本地。
- no-store:不进行强制缓存和协商缓存,直接拉取最新的资源。资源不缓存到本地。
-
private:如果有这个字段 比如:
Cache-Control: private, max-age=360000
,意思:中间层(代理)或者说CDN 不缓存此资源,使CDN失效,每次只能访问源服务器; 客户端可以缓存。 - public:CDN可以缓存: 客户端和代理服务器都可以缓存。
更多字段见 Cache-Control – HTTP | MDN
客户端请求资源时,
首次请求:服务端在资源返回时设置 http header 属性 Cache-Control 的值(response header),通常设置 max-age =xxx 开启强缓存。客户端根据 Cache-Control 的值对各种资源采取相应的缓存策略,对强缓存的资源在本地缓存起来。
二次请求:直接从本地拿。
注意:max-age
优先级要大于 Expires
。
补张图,别的地方拿的
协商缓存
协商缓存又称对比缓存、弱缓存。
涉及到的header 主要有
-
Last-Modified
/If-Modified-Since
(http1.0),匹配 Response Header 的Last-Modified
与 Request 的If-Modified-Since
是否一致 -
Etag
/If-None-Match
(http1.1),匹配 Response Header 的Etag
与 Request 的If-None-Match
是否一致
Last-Modified/
If-Modified-Since
Last-Modified 表示上一次 的修改时间。
客户端请求资源时
首次请求:服务端返回资源,在响应头(Response header)附带有 Last-Modified 属性,客户端拿到并在本地缓存(资源和Last-Modified标识)。
二次请求:只会发送header。请求头(Request header)自动带上 If-Modified-Since
, 值为 服务器返回的 Last-Modified 的值。服务器做校对修改时间是否一致(If-Modified-Since == Last-Modified ?),资源没变即返回304,客户端从本地缓存中取,否则重新发送请求。
Etag/
If-None-Match
Etag 表示 文件的唯一标识。
客户端请求资源时
首次请求:服务端返回资源,在响应头(Response header)附带有 Etag
属性,客户端拿到并在本地缓存(资源和 Etag
标识)。
二次请求:只会发送header。请求头(Request header)自动带上 If-None-Match
, 值为 服务器返回的 Etag
的值。服务器做校对文件唯一标识是否一致(If-None-Match == Etag ?),资源没变即返回304,客户端从本地缓存中取,否则重新发送请求。
Etag 要优于 Last-Modified,原因如下
- Last-Modified 最细的时间粒度为 S,Etag 有更细的时间粒度。
- 在1s内,如果某资源被修改(一次或者多次),Last-Modified 会错误的复用缓存,Etag 不会
http1.1版本 推出的 Etag 就是解决 http1.0 版本Last-Modified 存在的上述问题。
当然 Etag 也有缺点
- 计算 Etag 值需要性能损耗;
- 分布式服务器时依赖算法:分布式服务器存储的情况下,计算 Etag 的算法如果不一样,会导致浏览器从一台服务器上获得页面内容后到另外一台服务器上进行验证时现 Etag 不匹配的情况。
注意: Etag 优先级大于 Last-Modified
总结
强制缓存优先于协商缓存
简单理解,协商缓存就是每次都会每次请求资源时客户端都会去问服务器,资源能用吗(是否被修改),我要用最新的。
协商缓存需要配合 设置 Cache-Control:no-cache
使用(这一点,我是在 利用 koa 模仿缓存场景得出的)
补张图
一些场景
常见操作
- 新开页面窗口/前进后退/链接跳转/输入 url 回车:根据实际缓存策略处理,没有设置 no-cache 或 no-store,默认先走强制缓存路线
- 按钮刷新,F5 刷新,右键重新加载:将 cache-control 的 max-age 直接设置成了 0,让缓存立即过期,直接走协商缓存路线
- ctrl+F5 强刷:浏览器会强行设置 no-store,强制获取最新的资源。 同 控制台设置 禁用缓存 F5刷新。
常见策略配置
频率 | 可能会频繁更改,需要每次都询问。 | 可能每月修改 | 几乎不变 |
---|---|---|---|
Cache-Control | private, no-cache | Public, max-age=31536000 (一年甚至永久) | Public, max-age=31536000 (一年甚至永久),stale-while-revalidate(实验性字段)=86400 |
使缓存失效 | 每次都要询问,确保最新 | 改名字(hash值) | 改名字(hash值) |
Cache-Control: max-age=315360000
使用场景:CDN(一般永远不变)
效果:缓存10年(强缓存)
Cache-Control: max-age=31536000
使用场景:比如图片资源(几乎不变)
效果:缓存1年(强缓存)
Cache-Control: max-age=2592000
使用场景:比如js,css资源(按月迭代,较少频率改变)
效果:缓存1月(强缓存)
Cache-Control: no-cache
使用场景:webpack工程的 SPA单页面的入口 index.html(可能频繁改变)
效果:每次都要发起协商缓存,去询问资源是否变更,无变更则304重定向(协商缓存)
缓存大致流程
结尾
这种文章属于烂大街的文章,建议多阅读几篇。为了更好的理解http缓存,自己输出文章理解能比较深刻。
文末,写本文的过程中也解决了我的一些疑问:
- 本地空间(内存、磁盘)是浏览器在操作(缓存资源,获取标识,判断标识等)。这是个愚蠢的问题😂
- 协商缓存,缓存命中后向服务器发送的校验请求只有header(携带
Etag/Last-Modified
)而无body详见 前端 – HTTP缓存的疑问? – SegmentFault 思否
协商缓存要配合强缓存使用,如果不开启强缓存使用,协商缓存没有意义
大部分web
服务器默认开启协商缓存,且是同时开启last-modified
和Etag
缓存都是服务端控制,设置响应头部,客户端执行相应缓存策略。前端能做的事微乎其微,不过了解缓存机制对我们日常开发还是有帮助的。如果前端有 nginx 权限,那么就可以在http 缓存上大刀阔斧的优化。
网上看了许多文章,这里一一感谢👏🏻👏🏻👏🏻。
HTTP 缓存 – HTTP | MDN
什么是强缓存和协商缓存 – 掘金
浏览器缓存 – Javascript 编程基础 – SegmentFault 思否
什么是强缓存和协商缓存 – 掘金
如何制定前端资源的最佳缓存策略? – 掘金
浅谈 强制缓存/协商缓存 怎么用? – 掘金
前端面试常见的浏览器缓存(强缓存、协商缓存),代码实操 – 掘金
不废话,代码实践带你掌握 强缓存、协商缓存! – 掘金
史上最详细的经典面试题 从输入URL到看到页面发生了什么? – 掘金
浏览器缓存:memory cache、disk cache、强缓存协商缓存等概念_(memory cache)_Lvan的前端笔记的博客-CSDN博客
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net