![缓存头图.jpg][1] # 浏览器知识系列之浏览器缓存 ### 什么是缓存? 浏览器缓存是一种用于提高网站性能的技术,它通过在**本地磁盘,内存或缓存服务器**中存储网站的HTML、CSS、JavaScript、图片等文件副本,以便在用户再次访问相同的URL时,浏览器可以直接从本地或缓存服务器中加载这些资源,而**无需重新从服务器下载**。这样做可以减少网络带宽消耗,降低服务器的负担,并提升用户体验。 ### 说得这么玄乎,我要怎么看到缓存呢? 查看缓存的办法很简单,以`Chrome`浏览器为例子,打开开发者工具,进入`NetWork`页面,在`Size`这一栏我们就可以看到这样的资源加载情况: ![缓存1.jpg][2] 如果`size`是`memory cache`和`disk cache`就表示:浏览器并没有向服务器发送请求,而是直接读取了本地的缓存资源文件。至于什么是`memery cache`什么是`disk cache`,我们暂且按下不表,之后在介绍缓存位置的时候会介绍到。 ### 从3个方面入手 现在我们知道了,缓存,作为一个能极大提高用户浏览体验的存在,在我们上网的过程中几乎无时无刻在发生着作用。那么,理解浏览器的缓存机制对于开发同学来说就是非常重要的知识点,这篇文章将会从以下三个方面入手介绍浏览器缓存,以便大家都能构建出自己的一份浏览器缓存体系: 1. 强缓存 2. 协商缓存 3. 缓存位置 ### 1.强缓存 强缓存是一种浏览器缓存的策略,它指的是:**如果缓存资源有效,则直接使用缓存资源,不必再向服务器发起请求。** #### 设置强缓存的两种方式: ##### HTTP/1.0使用Expires `Expires`即过期时间,存在于服务端返回的**HTTP请求响应头**中,告诉浏览器在这个过期时间之前可以直接从缓存里面获取数据,无需再次请求。比如下面这样: `Expires: Mon, 04 Mar 2022 04:01:32 GMT` 表示资源在2012年03月04号4点01分过期,过期了就得向服务端发请求。这个方式看上去没什么问题,其实潜藏了一个问题:这个时间是一个**绝对时间**,它是服务器的时间,如果**客户端的时间和服务器端的时间不一致**,或者**用户修改了客户端的时间**,这样就会影响缓存命中的效果。针对这个问题,在HTTP/1.1中,浏览器有了新的解决方案。 ##### HTTP/1.1使用Cache-Control 在HTTP1.1中,新增了一个非常关键的字段:`Cache-Control`。这个字段也是存在于**HTTP请求响应头**中,它和`Expires`本质的不同在于它并没有采用具体的过期时间点这个方式,而是采用过期时长来控制缓存,对应的字段是`max-age`。比如这个例子:`Cache-Control:max-age=3600` 代表这个响应返回后在 3600 秒,也就是一个小时之内可以直接使用缓存。而且不止于此,`Cache-Control`还可以搭配其他不同属性来控制更多更复杂的场景下的缓存,比如: 1. **`public`:客户端和代理服务器都可以缓存**。因为一个请求可能要经过不同的**代理服务器**最后才到达目标服务器,那么结果就是不仅仅浏览器可以缓存数据,中间的任何代理节点都可以进行缓存。 2. `private`:表示响应资源**仅仅只能被获取它的浏览器端**缓存。它不允许任何中间者缓存响应的资源。 3. `no-cache`: 跳过当前的强缓存,发送HTTP请求,即直接进入**协商缓存**阶段。 4. `no-store`:非常粗暴,不进行任何形式的缓存。**(注意这里要和`no-cache`区分)** 5. `s-maxage`:与上文提到的max-age类似,这里的“s”代表共享,并且,这个指令一般仅用于控制代理服务器或CDN之类的中间者的缓存。 6. `no-transform`:中间代理有时会改变图片以及文件的格式,从而达到提高性能的效果。`no-transform`指令告诉中间代理不要改变资源的格式。 值得注意的是,当`Expires`和`Cache-Control`同时存在的时候,`Cache-Control`会被优先考虑。 ![缓存2.jpg][3] 那么,假如我们已经不在强缓存要求的缓存时间里了,也就是说**强缓存失效了**,接下来怎么办呢?别急,浏览器还有第二道缓存策略————协商缓存。 ### 协商缓存 协商缓存就是强制缓存失效后,浏览器携带缓存标识**(Tag)**向服务器发起请求,由服务器根据缓存标识**(Tag)**决定是否使用缓存的过程。 #### 设置协商缓存的两种方式: ##### HTTP/1.0使用Last-Modified和If-Modified-Since `Last-Modified`即最后修改时间,在浏览器请求了可缓存的资源后,服务器会在**响应头**中加入`Last-Modified`字段来指出资源最后一次修改的时间。 浏览器接收到后,如果再次请求,会在请求头中携带`If-Modified-Since`字段,这个字段的值也就是服务器上一次传来的`Last-Modified`的值。 当请求发送到服务器后,服务器会使用这个字段来**和资源的最后一次的修改时间来进行比较**,以此来判断资源是否做了修改: - 如果资源没有被修改,那么返回`304`状态码,让客户端使用本地的缓存。 - 如果资源已经被修改了,则返回修改后的资源。 ##### HTTP/1.1使用Etag和If-None-Match `ETag`是服务器根据当前文件的内容,给文件生成的**唯一标识**,只要里面的内容有改动,这个值就会变。和`Last-Modified`一样,服务器通过**响应头**字段把这个值给浏览器。 浏览器接收到后,如果再次请求,会在请求头中携带`If-None-Match`字段,这个字段的值也就是服务器上一次传来的`ETag`的值。 当请求发送到服务器后,服务器会使用这个字段来**和资源的最新的`ETag`进行比较**,以此来判断资源是否做了修改: - 如果资源没有被修改,那么返回`304`状态,让客户端使用本地的缓存。 - 如果资源已经被修改了,则返回修改后的资源。 ![缓存3.jpg][4] ##### 两者对比 1. 在**精准度**上,`ETag`优于`Last-Modified`。由于`ETag`是按照内容给资源上标识,因此能准确感知资源的变化。而`Last-Modified`就不一样了,它在一些特殊的情况并不能准确感知资源变化,主要有两种情况: 1. 编辑了资源文件,但是文件内容并没有发生改变,这样也会造成缓存失效。 2. `Last-Modified`能够感知的单位时间是秒,如果文件在 1 秒内改变了多次,那么这时候的` Last-Modified`就不能体现出修改了。 2. 在性能上,`Last-Modified`优于`ETag`,也很简单理解,`Last-Modified`仅仅只是记录一个时间点,而`Etag`需要根据文件的具体内容生成哈希值。 另外,如果两种方式都支持的话,服务器会优先考虑`ETag`。 ### 缓存位置 前面我们已经提到,当**强缓存命中**或者**协商缓存中服务器返回304**的时候,我们直接从缓存中获取资源。那这些资源究竟缓存在什么位置呢? 浏览器中的缓存位置一共有四种,按优先级从高到低排列分别是: 1. Service Worker 2. Memory Cache 3. Disk Cache 4. Push Cache 5. 【新增】prefetch cache 通过``标签预加载获得的内容,会出现在这个缓存位置里。可以在wbepack里异步引入+魔法注释配置。 #### Service Worker `Service Worker`是`Web Worker`进一步发展后的产物。SW是运行在浏览器背后的独立线程,一般可以用来实现缓存功能。使用SW的话,传输协议必须为HTTPS,因为它涉及请求拦截。SW的缓存与浏览器其他内置缓存不同,它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,并且它的缓存是持续性的。针对于Service Worker的理解,可以看我这一篇文章:[ServiceWorker实现离线缓存][5] #### Memory Cache `Memory Cache`指的是内存缓存,**从效率上讲它是最快的**,但是**从存活时间来讲又是最短的**。 一旦关闭标签页,内存中的缓存也就释放了,经过测试: 1. 第一次打开`baidu.com`,大部分资源从服务器中请求 2. 刷新页面,大部分资源从`Memory Cache`或者`Disk Cache`中获取 3. **关闭**该标签页,打开`baidu.com`, 大部分资源从`Disk Cache`中获取。 内存缓存主要包含当前页面中已经抓取到的资源,例如页面上已经下载好的**样式、脚本、图片**等。 #### Disk Cache `Disk Cache`就是存储在磁盘中的缓存,从**存取效率上讲是比内存缓存慢的**,但是他的优势在于存储容量和存储时长。绝大部分缓存来自`Disk Cache`,对于大文件,大概率存进磁盘,当前内存使用率高,也优先存入磁盘。 #### Push Cache `Push Cache`即推送缓存,这是浏览器缓存的最后一道防线。当以上三种缓存都没有命中时才会被使用。它只在会话中存在,一旦会话结束就释放了,缓存时间只有5分钟左右,同时也不严格执行HTTP头中的缓存指令。它是`HTTP/2.0`中的内容,虽然现在应用的并不广泛,但随着`HTTP/2.0`的推广,它的应用越来越广泛。这里就不多赘述了。 #### 以上四者的优先级? 我们先来看标准定义的资源请求遵循的顺序: ![普通优先级.png][6] 看起来是`Service Worker`优先于`Memory Cache`优先于`Disk Cache`。 但这是标准定义的资源请求流程,但是有追求的浏览器还会在`Service Worker`上面加一层 内存缓存层」,以 Chrome 为例。 ![Chrome优先级.png][7] `Memory Cache`作为第一公民,位于`Service Worker`之上。也就是命中了`Memory Cache`,就不会触发`Service Worker`的 `fetch`事件。而`Disk Cache`则位于原来的 HTTP 缓存层。 ### 总结 浏览器缓存机制其实很简单,流程就如这篇文章里的第一张图片一样: 1. 首先尝试确认强缓存,如果命中,则直接从缓存中拿取资源。 2. 如果没有命中强缓存,则发送请求自服务器,尝试协商缓存,如果命中,则会拿到304状态码,浏览器再从缓存中拿取资源。 3. 如果也没有命中协商缓存,那会从服务器中拿取最新资源。 4. 一般来说`Service Worker`优先于`Memory Cache`优先于`Disk Cache`优先于`Push Cache`。 [1]: http://120.25.166.245/usr/uploads/2022/03/2687907129.jpg [2]: http://120.25.166.245/usr/uploads/2022/03/2751135840.jpg [3]: http://120.25.166.245/usr/uploads/2022/03/1811237764.jpg [4]: http://120.25.166.245/usr/uploads/2022/03/361321031.jpg [5]: http://120.25.166.245/index.php/archives/15/ [6]: http://120.25.166.245/usr/uploads/2024/03/305250067.png [7]: http://120.25.166.245/usr/uploads/2024/03/3127579991.png 最后修改:2024 年 06 月 15 日 12 : 14 PM © 著作权归作者所有 赞赏 如果觉得我的文章对你有用,请随意赞赏 ×Close 赞赏作者 扫一扫支付 支付宝支付 微信支付
这篇文章不错!
哈哈哈,写的太好了https://www.lawjida.com/