@[TOC]浏览器缓存机制
浏览器缓存(http缓存)
简单来说,浏览器缓存其实就是浏览器保存通过HTTP获取的所有资源,是浏览器将网络资源存储在本地的一种行为。
缓存的资源去哪里了?
你可能会有疑问,浏览器存储了资源,那它把资源存储在哪里呢?如下看一个例子:
首次访问页面![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/6e9f3d87b19638e20f306cf91cfb613c.png)
关闭标签页,重新打开页面![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/a317dc82a834d59fe2a67838b3c6b054.png)
刷新页面![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/c2fce9b445fd5e1ed9d988d36fed0dcb.png)
memory cache和disk cache
from memory cache:就是将资源缓存到内存中,等待下次访问时不需要重新下载资源,而直接从内存中获取。
from disk cache:就是将资源缓存到磁盘中,等待下次访问时不需要重新下载资源,而直接从磁盘中获取。
名称 | 相同点 | 不同点 | 存储资源 |
---|---|---|---|
memory cache | 只能存储一些派生类资源文件(HTML页面中内嵌的图片或者脚本链接) | 退出进程时数据会被清除(即重新打开新的窗口,memory cache中数据被清除了,刷新不会) | 一般脚本、字体、图片会存在内存当中 |
diskcache | 只能存储一些派生类资源文件(HTML页面中内嵌的图片或者脚本链接) | 退出进程时数据不会被清除(即重新打开新的窗口,disk cache中数据没有被清除) | 一般非脚本会存在内存当中,如css等 |
因为CSS文件加载一次就可渲染出来,我们不会频繁读取它,所以它不适合缓存到内存中,但是js之类的脚本却随时可能会执行,如果脚本在磁盘当中,我们在执行脚本的时候需要从磁盘取到内存中来,这样IO开销就很大了,有可能导致浏览器失去响应。
三级缓存原理 (访问缓存优先级)
- 先在内存中查找,如果有,直接加载。
- 如果内存中不存在,则在硬盘中查找,如果有直接加载。
- 如果硬盘中也没有,那么就进行网络请求。
- 请求获取的资源缓存到硬盘和内存。
总述:浏览器读取缓存的顺序为memory –> disk –> 服务器请求。
浏览器缓存的优点
- 减少了冗余的数据传输。
- 减少了服务器的负担,大大提升了网站的性能。
- 加快了客户端加载网页的速度
浏览器缓存的分类
浏览器再向服务器请求资源时,首先判断是否命中强缓存,再判断是否命中协商缓存!
缓存过程分析
浏览器与服务器通信的方式为应答模式,即是:浏览器发起HTTP请求 – 服务器响应该请求。
如下图我们可以知道:
- 浏览器每次发起请求,都会先在浏览器缓存中查找该请求的结果以及缓存标识。
- 浏览器每次拿到返回的请求结果都会将该结果和缓存标识存入浏览器缓存中。
强制缓存(Expires和Cache-Control)
强制缓存就是向浏览器缓存查找该请求结果,并根据该结果的缓存规则来决定是否使用该缓存结果的过程。
强制缓存的情况主要有三种,如下:
- 不存在该缓存结果和缓存标识,强制缓存失效,则直接向服务器发起请求。
- 存在该缓存结果和缓存标识,但该结果已失效,强制缓存失效,则使用协商缓存。
- 存在该缓存结果和缓存标识,且该结果尚未失效,强制缓存生效,直接返回该结果。
强制缓存的缓存规则
当浏览器向服务器发起请求时,服务器会将缓存规则放入HTTP响应报文的HTTP头中和请求结果一起返回给浏览器,控制强制缓存的字段分别是Expires和Cache-Control,其中Cache-Control优先级比Expires高。
Expires
- Expires是HTTP/1.0控制网页缓存的字段;
其值为服务器返回该请求结果缓存的到期时间;
即再次发起该请求时,如果客户端的时间小于Expires的值时,直接使用缓存结果。 - 到了HTTP/1.1,Expire已经被Cache-Control替代
Cache-Control
cache-control:主要用于控制网页缓存 | 规则 |
---|---|
public | 所有内容都将被缓存(客户端和代理服务器都可缓存) |
private | 所有内容只有客户端可以缓存,Cache-Control的默认取值 |
no-cache | 客户端缓存内容,但是是否使用缓存则需要经过协商缓存来验证决定 |
no-store | 所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存 |
max-age=xxx (xxx is numeric) | 缓存内容将在xxx秒后失效 |
举例如下,max-age=0 缓存内容将在0秒后失效,即不进行强制缓存
协商缓存(Last-Modified / If-Modified-Since和Etag / If-None-Match)
协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程。
主要有以下两种情况:
- 协商缓存生效,返回304。
- 协商缓存失效,返回200和请求结果。
协商缓存的缓存规则
协商缓存的标识也是在响应报文的HTTP头中和请求结果一起返回给浏览器的,控制协商缓存的字段分别有:Last-Modified / If-Modified-Since和Etag / If-None-Match。其中Etag / If-None-Match的优先级比Last-Modified / If-Modified-Since高。
Last-Modified / If-Modified-Since
Last-Modified:是服务器响应请求时,返回该资源文件在服务器最后被修改的时间。
If-Modified-Since:是客户端再次发起该请求时,携带上次请求返回的Last-Modified值,通过此字段值告诉服务器该资源上次请求返回的最后被修改时间。
缓存判断条件:服务器的资源最后被修改时间大于If-Modified-Since的字段值,则重新返回资源,状态码为200;否则则返回304,代表资源无更新,可继续使用缓存文件。如下例子:
Etag / If-None-Match
Etag:是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成)
If-None-Match:是客户端再次发起该请求时,携带上次请求返回的唯一标识Etag值,通过此字段值告诉服务器该资源上次请求返回的唯一标识值。
缓存判断条件:会根据If-None-Match的字段值与该资源在服务器的Etag值做对比,一致则返回304,代表资源无更新,继续使用缓存文件;不一致则重新返回资源文件,状态码为200。如下例子:
注:Etag / If-None-Match优先级高于Last-Modified / If-Modified-Since,同时存在则只有Etag / If-None-Match生效。对于协商缓存,使用 Ctrl+F5强制刷新可以使得缓存无效。但是对于强缓存,在未过期时,必须更新资源路径才能发起新的请求(更改了路径相当于是另一个资源了,这也是前端工程化中常用到的技巧)。
总之
当浏览器再次访问一个已经访问过的资源时,它会这样做:
- 看看是否命中强缓存,如果命中,就直接使用缓存了。
- 如果没有命中强缓存,就发请求到服务器检查是否命中协商缓存。
- 如果命中协商缓存,服务器会返回 304 告诉浏览器使用本地缓存。
- 否则,返回最新的资源。
主要过程如下:
本地存储
Cookie:主要用于用户信息的存储,Cookie的内容可以自动在请求的时候被传递给服务器。
LocalStorage:数据将一直保存在浏览器内,直到用户清除浏览器缓存数据为止。
SessionStorage:其他属性同LocalStorage,只不过它的生命周期同标签页的生命周期,当标签页被关闭时,SessionStorage也会被清除。
DNS缓存
CDN缓存
疑问
使用webpack打包时,会为了更好地利用浏览器缓存,给生成的静态文件加上chunkhash,这样就能在文件内容变化时使文件名发生变化变化,保证浏览器会去服务器上读取最新的资源。我的疑问是,在引用的js或css文件名变化时,利用HtmlWebpackPlugin生成的index.html的内容也就会有所变化,但是index.html的文件名并没有变化,如何保证不会读取到缓存中的index.html呢?如果要靠etag和last-modified来保证index.html正确读取,那其他js文件不也可以这样不需要hash了?还是说浏览器对html文件和其他文件的缓存机制不一样?
学习参考链接: