浏览器缓存(Browser Caching)是为了节约网络的资源加速浏览,浏览器在用户磁盘上对最近请求过的文档进行存储,当访问者再次请求这个页面时,浏览器就可以从本地磁盘显示文档,这样就可以加速页面的阅览。
缓存资源
缓存资源分为 memory cache(内存缓存) 和 disk cache(磁盘缓存)
memory cache
不访问服务器,直接读缓存,从内存中读取缓存。此时的数据时缓存到内存中的,当kill进程后,也就是浏览器关闭以后,数据将不存在。但是这种方式只能缓存派生资源。
我们经常看到的返回200显示from memory cache,其实就是取得是内存里的缓存
disk cache
不访问服务器,直接读缓存,从磁盘中读取缓存,当kill进程时,数据还是存在。这种方式也只能缓存派生资源
当我们关闭页面,在缓存还未过期的时候访问的一些访问过的派生资源时,就显示200 from disk cache其实就是磁盘缓存
三级缓存原理 (访问缓存优先级)
- 先在内存中查找,如果有,直接加载。
- 如果内存中不存在,则在硬盘中查找,如果有直接加载。
- 如果硬盘中也没有,那么就进行网络请求。
- 请求获取的资源缓存到硬盘和内存。
后面会有例子来证明优先级是否正确
失效策略分类
按照失效策略分类,就包括两种 强缓存 和 协商缓存
强缓存
强缓存主要包括 expires
和 cache-control
。
对比来加深理解
区别 | expires | cache-control |
---|---|---|
http版本 | 1.0 | 1.1 |
时间 | 绝对时间 | 相对时间 |
优先级 | 低于cache-control | 高于expires |
例子 | expires:Mar, 06 Apr 2020 10:47:02 GMT | Cache-Control:max-age=3600 |
代表意义(例子为例) | 资源的失效时间,在此字段值之前则命中缓存 | 资源的有效期是 3600 秒 |
缺点 | 由于失效时间是一个绝对时间,因当服务器与客户端时间偏差较大时,就会导致缓存混乱 | 无法兼容HTTP1.0 |
实践
通过实践来看看是否符合原理
用koa2的脚手架创建一个项目
koa2 -e koa2-learn
(koa2-learn代表项目名称)
创建完成之后在public文件夹里新建一个html以及在images放一个图片
1 | <!-- public/index.html --> |
在app.js文件中设置cache-control
注意:新添加的代码块必须得在koa-static
引用之前才会生效
1 | // 新添加 --start |
将项目跑起来看看效果
可以发现设置的cache-Control
生效了,第一次请求浏览器会把图片存进了磁盘(disk cache
)和内存(memory cache
)中。
取内存资源
紧接着我们来刷新一下,根据三级缓存原理,我们会先在内存中找资源
发现图片是直接从内存中取的,符合
取磁盘资源
memory cache
在关闭进程的时候会释放掉其中的资源,现在我们关闭浏览器,重新打开看看当内存没有该图片的时,是否会取磁盘内存里的图片
发现图片取了磁盘内存,符合
验证cache-control
我们设置的缓存有效期是300s,当过了300s后我们重新刷新一下,来验证一下缓存是否失效
可以发现缓存失效了,跟我们第一次请求的时候一模一样
协商缓存
协商缓存就是通过服务器来判断缓存是否可用,服务器则根据header的信息( Last-Modify/If-Modify-Since
和 ETag/If-None-Match
)来判断是否命中协商缓存,如果命中,则返回 304 ,告诉浏览器资源未更新,可使用本地的缓存。
Last-Modify/If-Modify-Since
浏览器第一次请求一个资源的时候,服务器返回的 header 中会加上 Last-Modify
,Last-modify
是一个时间标识该资源的最后修改时间。
当浏览器再次请求该资源时,request 的请求头中会包含If-Modify-Since
,该值为缓存之前返回的 Last-Modify
。服务器收到 If-Modify-Since
后,根据资源的最后修改时间判断是否命中缓存。
如果命中缓存,则返回 304,并且不会返回资源内容,并且不会返回 Last-Modify。
缺点:短时间内资源发生了改变,Last-Modified 并不会发生变化。
ETag/If-None-Match
与 Last-Modify/If-Modify-Since
不同的是,Etag/If-None-Match
返回的是一个校验码。ETag
可以保证每一个资源是唯一的,资源变化都会导致 ETag
变化。
web服务器收到请求后发现有头If-None-Match
则与ETahe
进行对比,相同则返回304
不同则返回200
。
常见问题
Etag与Last-Modified有什么区别?
Etag
更倾向于标识资源是否有变更,而Last-Modified
更倾向于含有时间状态的数据如果Etag和Last-Modified同时存在,服务器会先检测哪一个?
服务器会先检测
Etag
再去检测Last-Modified
,因为Etag发生改变的话那么资源的内容一定发生了变化,而Last-Modified
发生了变化资源内容不一定发生改变
Etag实践
安装下面两个依赖
yarn add koa-conditional-get
,yarn add koa-etag
1 | const conditional = require('koa-conditional-get') |
看一下第一次请求的时候效果
发现已经有etag
的值了,现在我们来刷新看看
当我们再次请求的时候Request Headers上的If-None-Match
已经带上了Etag
的值了
接下来我们将一个新的图片跟现有图片重命名覆盖掉,刷新请求
发现协商缓存失效了,Request Headers上的If-None-Match
还是上一张图片的Etag
值,与Response Headers返回来的Etag
不相等所以重新请求新资源返回200。