Twlig / issuesBlog

MIT License
3 stars 0 forks source link

浏览器缓存机制 #82

Open Twlig opened 2 years ago

Twlig commented 2 years ago

浏览器缓存机制

一、什么是浏览器缓存

浏览器缓存其实就是浏览器保存通过HTTP获取的所有资源,是浏览器将网络资源存储在本地的一种行为。

二、缓存的资源去哪里了

内存缓存(memory cache)和硬盘缓存(disk cache)。

image

CSS文件加载一次就可渲染出来,我们不会频繁读取它,所以它不适合缓存到内存中,但是js之类的脚本却随时可能会执行,如果脚本在磁盘当中,我们在执行脚本的时候需要从磁盘取到内存中来,这样IO开销就很大了,有可能导致浏览器失去响应。

三级缓存原理 (访问缓存优先级)

  1. 先在内存中查找,如果有,直接加载。
  2. 如果内存中不存在,则在硬盘中查找,如果有直接加载。
  3. 如果硬盘中也没有,那么就进行网络请求。
  4. 请求获取的资源缓存到硬盘和内存。

三、浏览器缓存的分类

  1. 强缓存
  2. 协商缓存

浏览器再向服务器请求资源时,首先判断是否命中强缓存,再判断是否命中协商缓存! (先判断强缓存,再协商缓存)

强制缓存是直接使用浏览器缓存的资源;协商缓存是向服务器请求,服务器响应缓存规则,浏览器根据服务器的响应决定是从浏览器缓存获取资源还是服务器获取资源。

强制缓存:控制字段分别是Expires和Cache-Control,其中Cache-Conctrol的优先级比Expires高。

协商缓存:当Expires和Cache-Control中有一方过期,则浏览器会携带当前资源的版本信息,向服务器请求资源,服务器根据浏览器发来的版本信息判断资源是否需要更新,如果要更新则发送更新的资源,并设置好新的Expires和Cache-Control字段一起返回给浏览器。如果资源不需要更新,则响应304,让浏览器采用本地缓存资源,并设置新的Expires和Cache-Control字段一起返回给浏览器。

四、浏览器缓存的优点

1.减少了冗余的数据传输

2.减少了服务器的负担,大大提升了网站的性能

3.加快了客户端加载网页的速度

五、强缓存

浏览器在加载资源时,会先根据本地缓存资源的 header 中的信息判断是否命中强缓存,如果命中则直接使用缓存中的资源不会再向服务器发送请求。

这里的 header 中的信息指的是 expires 和 cahe-control.

1、Expires

该字段是 http1.0 时的规范,它的值为一个绝对时间的 GMT 格式的时间字符串,比如 Expires:Mon,18 Oct 2066 23:59:59 GMT。这个时间代表着这个资源的失效时间,在此时间之前,即命中缓存。这种方式有一个明显的缺点,由于失效时间是一个绝对时间,所以当服务器与客户端时间偏差较大时,就会导致缓存混乱。

2、Cache-Control

Cache-Control 是 http1.1 时出现的 header 信息,主要是利用该字段的 max-age 值来进行判断,它是一个相对时间,例如 Cache-Control:max-age=3600,代表着资源的有效期是 3600 秒。cache-control 除了该字段外,还有下面几个比较常用的设置值:

no-cache:需要进行协商缓存,发送请求到服务器确认是否使用缓存。

no-store:禁止使用任何缓存,每一次都要重新请求数据。

public:可以被所有的用户缓存,包括终端用户和 CDN 等中间代理服务器。

private:只能被终端用户的浏览器缓存,不允许 CDN 等中继缓存服务器对其缓存。

max-age=xxx (xxx is numeric):缓存内容将在xxx秒后失效。

Cache-Control 与 Expires 可以在服务端配置同时启用,同时启用的时候 Cache-Control 优先级高。

六、协商缓存

当强缓存没有命中的时候,浏览器会发送一个请求到服务器,服务器根据 header 中的部分信息来判断是否命中缓存。如果命中,则返回 304 ,告诉浏览器资源未更新,可使用本地的缓存。

这里的 header 中的信息指的是 Last-Modify/If-Modify-Since 和 ETag/If-None-Match.

1、Last-Modify/If-Modify-Since

浏览器第一次请求一个资源的时候,服务器返回的 header 中会加上 Last-Modify,Last-modify 是一个时间标识该资源的最后修改时间。

当浏览器再次请求该资源时,request 的请求头中会包含 If-Modify-Since,该值为缓存之前返回的 Last-Modify。服务器收到 If-Modify-Since 后,根据资源的最后修改时间判断是否命中缓存。

如果命中缓存,则返回 304,并且不会返回资源内容,并且不会返回 Last-Modify。

缺点:

1、短时间内资源发生了改变,Last-Modified 并不会发生变化。

2、周期性变化。如果这个资源在一个周期内修改回原来的样子了,我们认为是可以使用缓存的,但是 Last-Modified 可不这样认为,因此便有了 ETag。

2、ETag/If-None-Match

与 Last-Modify/If-Modify-Since 不同的是,Etag/If-None-Match 返回的是一个校验码。ETag 可以保证每一个资源是唯一的,资源变化都会导致 ETag 变化。服务器根据浏览器上送的 If-None-Match 值来判断是否命中缓存。

与 Last-Modified 不一样的是,当服务器返回 304 Not Modified 的响应时,由于 ETag 重新生成过,response header 中还会把这个 ETag 返回,即使这个 ETag 跟之前的没有变化。

Last-Modified 与 ETag 是可以一起使用的,服务器会优先验证 ETag,一致的情况下,才会继续比对 Last-Modified,最后才决定是否返回 304。

七、浏览器主动获取资源变化

浏览器无法主动得知服务器上的 a.js 资源变化了。

不管用 Expires 还是 Cache-Control,他们都只能够控制缓存是否过期,但是在缓存过期之前,浏览器是无法得知服务器上的资源是否变化的。只有当缓存过期后,浏览器才会发请求询问服务器。

方案

输入网址时是访问一个 html 文件,html文件中会引入 js、css 、图片等资源。

那不让 html 文件缓存,每次访问 html 都去请求服务器。所以浏览器每次都能拿到最新的html资源。

a.js 内容更新的时候,我们修改一下 html 中 a.js 的版本号。

<script src="http://test.com/a.js?version=0.0.1"></script>
<script src="http://test.com/a.js?version=0.0.2"></script>

所以,通过设置html不缓存,html引用资源内容变化则改变资源路径的方式,就解决了无法及时得知资源更新的问题。

当然除了以版本号来区分,也可以以 MD5hash 值来区分。 如

<script src="http://test.com/a.【hash值】.js"></script>

使用webpack打包的话,借助插件可以很方便的处理。

八、浏览器缓存过程

image

参考文章: