浏览器缓存策略
介绍
为什么需要浏览器缓存
当你第一次访问页面的时候,浏览器会自动下载所有与这个页面有关的资源,例如 CSS、网页字体、JavaScript脚本。
但是,在很短一段时间内,当你第二次访问网页时,这些资源往往是不变的。如果这个时候仍然再加载这些 CSS、网页字体、JavaScript脚本,那么,不但增加了服务器的负担,也增加了请求的时间,这对双方都是十分不利的。
如果这个时候能够把一些很少变化的资源缓存起来,那么下次加载页面时,直接从硬盘读取数据,而不从服务器加载,那么既减少了服务器的负担,也减少了网页的加载时间。
总的来说,使用浏览器缓存,可以:
- 对于服务器:缓解服务器压力,减小服务器带宽
- 对于客户:减少内容加载时间,节省流量
在开发者工具中查看缓存情况
这里以京东首页为例。下图是第二次加载网页时,浏览器开发者工具的例子:
可以发现,有不少的资源被标注为了 disk cache
或 内存缓存。这些被标注的资源都是从本地加载的,而不是通过网络加载的。
在网络面板上面,有一个 停用缓存 的按钮。点击它以后可以禁用浏览器的缓存,从而观察网页首次加载的情况。
什么时候缓存
这一部分,我们就要探讨:什么时候使用缓存,什么时候不使用缓存。
浏览器提供了下面的两种策略来解决这个问题:
- 强缓存策略
- 协商缓存策略
强缓存策略
强缓存是说,如果本地已经有某个资源的缓存,而且没有过期,那么就直接使用本地的缓存,否则从服务器上加载数据。
我们结合流程图来理解强缓存:
graph LR START(开始)-->TRYLOAD[请求某个资源]-->CHECK{缓存是否存在且未过期}--是-->USECACHE[使用缓存]-->END(结束) CHECK--否-->LOADRESOURCES[发起网络请求]-->END(结束)
了解了整个流程之后,我们就来看看它是怎么实现的。
Expires
在 HTTP 1.0 中,强缓存是通过 Expires
Header 控制的。
1 | Expires: Fri, 24 Oct 2031 18:22:16 GMT |
但是,这个字段功能十分局限,只能指定一个绝对时间。
- 首先,这给出的是一个绝对时间。这意味着如果用户修改了电脑时间,那么这个设置可能就失效了。
- 其次,它对于时间的格式有着苛刻的要求,如果传入了一个错误的时间字符串,这个字段就会失效。
Cache-Control
于是,在HTTP 1.1 中,新增了一个 Cache-Control
Header 字段。
接下来对一些常用的值进行讲解。
缓存时间
max-age=<seconds>
:通常用于设置一个缓存的最大有效时间,单位为秒s-maxage
: 通常用于客户端和服务器中间的代理服务器的有效时间,如果指定了这个,那么对于代理服务器来说,max-age
会被忽略。
缓存的作用域
public
:资源可以被客户端和客户端和服务器中间的代理服务器缓存。private
:默认。资源只有客户端可以缓存。
缓存策略
must-revalidate
:如果超过了 max-age 的时间,浏览器必须重新请求验证资源。no-cache
:无论本地副本是否过期,使用之前必须发送请求来验证资源是否过期。no-store
: 禁止使用缓存,资源每次都从网络中加载。
具体的属性信息,可以参考:MDN
常用的一些值:
- 禁止缓存:
Cache-Control: no-store
- 设置缓存时间为1小时:
Cache-Control:public, max-age=3600
Cache-Control
和Expires
如果同时指定了,那么Cache-Control
会覆盖Expires
。
强缓存有一个缺点:一旦指定了这两个字段,浏览器在一定时间内就不会再次从服务器加载数据了。一旦这个时候数据发生了更新,用户如果没有清理浏览器缓存,拿到的仍然是旧的数据。
协商缓存策略
当强缓存失效后,浏览器携带缓存标识向服务器发起请求,浏览器根据响应结果来判断是否继续使用缓存。
- 如果响应304,则表示资源没有发生改变,浏览器继续使用缓存。
- 如果响应200,则表示缓存失效,服务器返回新的资源。
尽管浏览器仍然请求了服务器,但是如果资源没有发生更改,服务器无需返回新的资源,从而减少了网络传输的体积。
同样的,我们结合流程图来解释整个流程:
graph LR START(开始)-->TRYLOAD[请求某个资源]-->CHECK{缓存是否存在且未过期}--是-->USECACHE[使用缓存]-->END(结束) CHECK--否-->REQUEST[携带标识发起网络请求]-->ISEXPIRED{服务器是否返回403}--是-->USECACHE ISEXPIRED--否-->LOADRES[加载新的资源]-->END
协商缓存通常是结合一对请求响应头来实现的:
- Last-Modified 和 If-Modified-Since
- Etag 和 If-None-Match
接下来我们来分别讨论这两对响应头。
Last-Modified 和 If-Modified-Since
在第一次加载资源的时候,服务器会返回一个 Last-Modified
字段:
1 | Last-Modified: Fri, 10 May 2019 03:38:54 GMT |
浏览器得到响应后,会将数据和这个信息一起存储。
当缓存过期之后,浏览器便会在请求头上 增加一个名为 If-Modified-Since
、值为 Last-Modified
字段。服务器通过比对 Last-Modified
和 If-Modified-Since
,来判断缓存是否过期。
但是这种方法有一些问题:
- 这个字段的最小时间单位为秒,这意味着资源更新的速度比秒级还小,这个字段就没办法使用了。
- 如果文件是动态生成的,那么这个时间也是实时更新的,哪怕文件内容没有变,这个字段也没法用了。
Etag 和 If-None-Match
这个整体流程和上一小节是一致的,其中,Etag
对应 Last-Modified
,If-Modified-Since
对应If-None-Match
。
只不过Etag
使用文件的特殊标识(例如修改时间的 hash或文件值的hash)来代替时间。这样就规避了上面方法的两个问题。
在哪里存的
我们刚刚探讨了在浏览器中,什么时候使用缓存,这一部分,我们探讨浏览器缓存的存储位置。
在浏览器中,缓存保存在下面四个位置:
- Service Worker
- Memory Cache
- Disk Cache
- Push Cache
Service Worker
PWA(Progressive Web Apps,渐进式 Web 应用) 是一项新技术,它综合运用各种Web API,使得Web App 能够拥有像原生应用一样的体验。
其中 Service Worker 是实现PWA的一个重要技术。Service Worker 是运行在浏览器的一个独立的线程,相当于界面和Web服务器之间的一个代理,通过它,我们可以自由地控制:缓存什么文件、如何匹配缓存、如何读取缓存。它可以让网页中的资源进行离线缓存,使得在网络较差或者离线的时候,也能够对网页进行正常的操作,从而提高用户体验。
Service Worker 和 PWA 的具体内容会来未来的某一篇文章中提到。
Memory Cache
相比于磁盘,内存的速度是相当快的,所以如果网页没有关闭,而只是刷新一下,或者网页内的跳转,那么大部分内容会被载入 Memory Cache。
但是 Memory 一般间隔很短,一旦网页关闭了,对应的浏览器进程也就关闭了,缓存便被释放了。
Disk Cache
顾名思义,Disk Cache是存储在硬盘的。相比于Memory Cache,Disk Cache可以较长时间来保存缓存。但是,毕竟硬盘的速度比内存要慢一些,所以其访问速度比Memory Cache慢一些。
Push Cache
Push Cache是 HTTP/2 中的内容。
如果上面的三种缓存都没有命中,那么就会采用这种缓存。它只在会话(Session)中存在,一旦会话结束就被释放,并且缓存时间也很短暂,在Chrome浏览器中只有5分钟左右,同时它也并非严格执行HTTP头中的缓存指令。
加载顺序
当一个资源进行加载时,浏览器会按照下面的策略来按顺序来加载缓存,这称为 三级缓存原理。
- 查找内存缓存,如果存在,那么从内存中加载
- 如果内存缓存不存在,那么去磁盘中查找,找到便从磁盘中加载。
- 否则,进行网络请求,并将请求后符合条件的资源存入内存和磁盘中
参考资源
- 一文读懂前端缓存 作者:小蘑菇哥哥 链接:https://juejin.cn/post/6844903747357769742
- 前端浏览器缓存知识梳理 作者:黑色的枫 链接:https://juejin.cn/post/6947936223126093861
- web性能优化之:no-cache与must-revalidate深入探究 作者:程序猿小卡_casper 链接:https://segmentfault.com/a/1190000007317481
- 标题: 浏览器缓存策略
- 作者: ObjectKaz
- 创建于: 2021-11-04 13:23:47
- 更新于: 2021-11-11 03:47:40
- 链接: https://www.objectkaz.cn/e1646e2e9feb.html
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。