前几天,有个朋友问了我一个问题,他说本身的html设置了cache-control的max-age,为何chrome浏览器第一次加载的时候是200,以后就走了304。针对这个问题,引出了如下知识。html
缓存机制是提升资源使用效率最有效的方法。它的思想就是创建一个资源池,webkit请求资源的时候,先从资源池中寻找目标文件,若是有则使用,若是没有则经过网络请求。 浏览器常见的缓存分为强缓存和协商缓存。下面咱们用一张流程图来介绍一下强缓存和协商缓存。前端
强缓存:浏览器不发送任何请求,直接从本地缓存中读取文件。Status Code: 200 OK。web
协商缓存:浏览器发送请求,经过服务器来断定是否读取本地缓存。Status Code:304 Not Modified。算法
咱们简单说一下流程图。 浏览器发送请求时,会判断当前环境是否存在缓存。若是不存在缓存,则发起请求,客户端完成接受及缓存工做。若是存在缓存,则先判断缓存是否过时。chrome
那么怎么断定缓存是否过时呢?浏览器
主要有两个字段,一个是Expires
,一个是Cache-Control
。缓存
Expires
是Http1.0的规范,它是一个绝对时间的字符串,告知浏览器在该时间以前可以使用资源。服务器
Cache-Control
是Http1.1的规范,主要利用max-age
设置一个相对时间表明过时时间。Cache-Control
也可设置其它值,经常使用的就是max-age
及no-cache
。网络
所以,经过Cache-Control
完成缓存是否过时的判断,若是没有过时,则读取本地缓存文件,此时完成的是强缓存的读取。若是已通过期,则强缓存读取失败,进入协商缓存阶段。字体
那么协商缓存是怎么处理的呢?
协商缓存主要有两对值,分别为Etag
/If-None-Match
(默认是对文件内容计算获得的Hash值)和Last-Modified
/If-Modified-Since
(浏览器向服务器发送资源最后的修改时间),他们是成对出现的。
Etag
是Http1.1规范,是由服务器生成返回给前端。在协商缓存时,若是存在Etag
,则请求服务器时会带上If-None-Match
,这个If-None-Match
的值就是服务器返回给前端的Etag
的值。
Last-Modified
是Http1.0规范,是文件在服务器端最后被修改的时间。在协商缓存时,若是Last-Modified
存在,则请求服务器时会携带if-modified-since
,该字段为上次向服务端请求时返回的Last-Modified
。
若是以上两对至少存在一对,则浏览器向服务器发送请求,并携带相关字段,交由服务器断定是否进行协商缓存。在服务端,Etag
的优先级要高于Last-Modified
。所以服务器先进入Etag
的判断,再判断Last-Modified
。若是判断经过,则返回304,浏览器读取本地缓存。若是判断未经过,则返回200即文件资源,浏览器接受并从新完成缓存。
以上就是整个浏览器请求时,关于缓存的一系列操做。
那么Webkit内部是如何实现缓存机制的呢?咱们都知道,资源加载是网页加载和渲染的第一步,也是最为关键的一步。网页中依赖的资源有不少,一般包括HTML/JS/CSS/图片/SVG/视频等文件,这些类型的文件在Webkit内部都有不一样的类来表示。Webkit主要包含如下资源类:
CachedResource
。其中,HTML属于
MainResource
类,与之对应的是
CachedRawResource
类。
为何全部的类前面都带有Cached呢?由于全部对资源的请求,默认都先走缓存,再决定是否向服务器发起请求。这个缓存机制的思想就是经过创建一个缓存池,当Webkit须要资源的时候,默认去缓存池中寻找。
靠什么寻找?靠的是URL来寻找,正好符合了URL统一资源定位符的气质。若是匹配则使用,若是不匹配则建立CachedResource
下的一个对象,例如图片则建立CachedIamge
对象,并经过网络模块获取。此时,咱们指的这个缓存就是内存缓存,也就是咱们在浏览器中见到的200 from memory,那么何时是200 from disk呢?后面咱们再来介绍。
上面说到了Webkit中的资源,那怎么加载资源,确定得有相应的加载器。
Webkit主要有三种加载器。
第一种就是特定的加载器,好比加载image就有特定的图片加载器,加载js就有特定的js加载器,加载字体就要特定的字体加载器;
第二种是缓存机制的资源加载器,就是第一种特定加载器先来缓存机制的资源加载器中寻找缓存资源;
第三种是通用的资源加载器,是在Webkit须要从网络或者文件系统获取资源时的加载器。
说到第三种加载器,它是从网络或者文件系统获取资源,那么咱们想说的200 from disk也就是在此发生。即from disk 发生在 from memory以后。为何咱们说from disk是经过网络模块获取呢?由于在Webkit中,网络模块包含磁盘缓存。
说到这咱们简单说下from disk和from memory。
from memory,字面意思就是来自内存,其实这里表达的也就是字面的意思。指的是咱们当前加载的这个资源是从内存中获取的。这种请求不走浏览器,关闭页面时,内存会释放,再次打开也不会出现from memory。
from disk,也是字面意思,就是资源来自磁盘。来自磁盘中的数据确定是咱们以前缓存过得,它不会随着浏览器的关闭而消失,咱们每每打开浏览器时会出现的就是from disk。
因此顺序就是from memory => from disk => http request
为何咱们刷新页面的时候在network有些是from memory有些是from disk而有些是从新加载呢?
由于Webkit中的缓存池确定是有生命周期的,不可能让缓存池一直变大,不然就会内存溢出。所以,必须有相应的机制来控制缓存池的大小。Webkit采用的是LRU算法即最近最少使用算法。该算法是内存管理常见的页面置换算法,它选择最近最久未使用的页面予以淘汰,以此来保障资源池的大小。咱们说的这个资源池指的是内存缓存的资源池。那么硬盘缓存呢?
硬盘缓存中,Webkit会维护一个缓存资源表,该表中的关键字就是URL。根据浏览器的状况来更新这个缓存表。同时,Webkit也使用LRU算法来控制这张表的大小。
以上就是Webkit中的基本缓存策略,那么咱们回到文章开头提到的那个问题,为何在Chrom浏览器中是304而不是200呢?查阅了相关的资料,找到了这样一句话。
In particular, main resource loads do not get the benefits of WebKit’s memory cache.
这里讲到的main resource就跟咱们前面介绍的HTML属于MainResource
类同样,HTML文件并不走memory cache,所以在Chrome浏览器中会出现304,而不会出现200。其实,咱们能够认为这是Webkit替咱们往前迈了一步。在目前咱们开发网页时,一般不会给HTML设置缓存或者设置很短期的缓存,以保证文件的及时更新,而Webkit走在了最前面。咱们在此说的是Webkit,像FireFox浏览器则不是这样。