- 缓存的类型
- 缓存位置
- 缓存过程分析
- 缓存策略的实际场景应用
- 浏览器的本地存储
浏览器会把一些重复请求的数据保存在缓存中,可以很好的加快请求响应速度,提高了用户体验,但是一些情况下又会缓存已经过时的老数据,使得页面展示错误。
浏览器会根据缓存机制决定使用缓存还是再次向服务器发送请求。
- from memory cache:内存缓存
快速读取:将编译解析后的文件,直接存入该进程的内存中。
时效性:一旦该进程关闭,则该进程的内存则会清空。 - from disk cache:硬盘缓存
将缓存写入硬盘文件中,读取缓存需要对该缓存存放的硬盘文件进行I/O操作,然后重新解析该缓存内容,读取复杂,速度比内存缓存慢。
一般情况下,css文件和大的文件使用硬盘缓存,普通html和js文件使用内存缓存
1.缓存的类型
从缓存类型来说有两种,强缓存和协商缓存。
- 强缓存:不需要发送 HTTP 请求;
- 协商缓存:需要发送HTTP请求;
发送 HTTP 请求之前,浏览器会先检查强缓存,如果命中直接使用,否则就进入协商缓存。
(1)强缓存
在请求头中设置Cache-Control字段实现,可以设置一个具体的过期时长:
Cache-Control: max-age=300 // 300s后过期
- max-age:表示缓存存活时间
- no-cache:表示不进行强缓存验证,而是用协商缓存来验证
- no-store:所有内容都不会被缓存,既不使用强缓存,也不使用协商缓存
- public:客户端和代理服务器都可以缓存,响应可以被中间任何一个节点缓存
- private:只有客户端可以缓存,中间节点不允许缓存
- s-max-age:作用同 max-age,但是表示代理缓存,且优先级更高
根据HTTP header 中的缓存字段来判断哪些资源需要缓存,哪些已经过期了需要重新请求获取。适合缓存服务端就会在相应头添加Cache-Control
(2)协商缓存(对比缓存)--服务端缓存策略
服务器判断客户端资源,是否和服务端资源一样;一致则返回304,否则返回200和最新资源。
- 浏览器携带缓存标识(tag)向服务端发送请求;
- 服务端会根据缓存标识(tag)来决定是否使用缓存;
资源标识在Response Headers中设置:
1)Last-Modified(资源最后修改时间)
2)ETag (资源的唯一标识)
两者的实现方式是类似的,执行过程如下:
- 浏览器第一次向服务器请求资源;
- 服务器返回资源,会在响应头中添加 Last-Modified(ETag),值为最后修改时间;
- 当浏览器接收后缓存文件和这个 header;
- 浏览器再次请求该资源时,检测到 Last-Modified(ETag),就在请求头添加 If-Modified-Since ( If-None-Match)这个字段,值为 Last-Modified(ETag);
- 服务器接收到请求,根据 If-Modified-Since( If-None-Match)与资源的最后修改时间做对比;
- 若结果相同,则返回 304 状态码和一个空的响应体,告诉浏览器使用缓存;
- 若结果不同,返回 200 状态码和请求的资源,同时在响应头中更新 Last-Modified;
二者的区别:
- ETag的准确度更高,Last-Modified只能精确到秒级;
- Last-Modified性能更好(不一定,当资源被重复生成,而内容不变Etag更适合);
(3)刷新对缓存的影响
正常刷新:强制缓存和协商缓存都有效;
手动刷新:强制缓存失效,协商缓存有效;
强制刷新:强制缓存和协商缓存都失效;
2.缓存位置
缓存的具体位置:
- Service Worker
- Memory Cache
- Disk Cache
- Push Cache
(1)Service Worker
Service Worker 是运行在浏览器背后的独立线程,也就是说他脱离了浏览器的窗体,无法直接访问 DOM。功能上主要能实现:离线缓存、消息推送、网络代理等。比如离线缓存就是 Service Worker Cache。
特点:
- 借鉴了 Web Worker
- 传输协议必须是 HTTPS
- 可以自由的控制缓存哪些文件、如何匹配读取缓存
- 且缓存是持续性的
(2)Memory Cache
内存缓存,主要包含的是当前中页面中已经抓取到的资源,例如页面上已经下载的样式、脚本、图片等。
特点:
- 读取效率高,但是持续时间短,会随着进程的释放而释放
- 几乎所有的请求资源都能进入 memory cache,分为 preloader 和 preload
- 在从 memory cache 读取缓存时,浏览器会忽视 Cache-Control 中的一些 max-age、no-cache 等头部设置,除非设置了 no-store 这个头部设置
preloader
preloader 是页面优化的常见手段之一,它的主要作用是 在浏览器打开一个网页的时候,能够一边解析执行 js/css,一边去请求下一个资源,而这些被 preloader 请求来的资源就会被放入 memory cache 中,供之后的解析执行操作使用。
preload
preload 和 preloader 长得非常相似,它能够显式指定预加载的资源,这些资源也会被放进 memory cache 中,例如
<link rel="preload">
(3)Disk Cache
Disk Cache,也叫 HTTP Cache,是存储在硬盘中的缓存。
特点:
- 持续时间长,是实际存在于文件系统中的缓存
- 比内存缓存慢,但是优势在于存储容量大
在所有浏览器缓存中,Disk Cache 是覆盖面最大的,根据HTTP header 中的缓存字段来判断哪些资源需要缓存,哪些已经过期了需要重新请求获取。
前面提到的强缓存和协商缓存也是属于 Disk Cache,他们最终都存储在硬盘里。
Disk Cache 和 Memory Cache 之间的对比:
- 比较大的 JS、CSS 文件会被丢在硬盘中存储,反之则存储在内存中
- 当前系统内存使用率比较高的时候,文件优先进入磁盘当前系统内存使用率高的话,文件优先存储进硬盘
(4)Push Cache
Push Cache(推送缓存),它是浏览器缓存的最后一道防线,当以上三种缓存都没有命中的时候,它才会被使用。
它只会在会话(Session)中存在,一旦会话结束就会被释放,并且缓存时间非常短,在 Chrome 浏览器中只有 5 分钟。
3.缓存过程分析
从浏览器发起 HTTP 请求到获得请求结果,可以分为以下几个步骤:
- 浏览器第一次发起 HTTP 请求,在浏览器缓存中没有发现缓存结果和缓存标识
- 因此向服务器发起 HTTP 请求,获得该请求的结果以及缓存规则(也就是 Last-Modified 或者是 ETag)
- 浏览器把响应内容存入 Disk Cache,把响应内容的引用存入 Memory Cache
- 把响应内容存入 Service Worker 的 Cache Storage
下一次请求相同资源的时候:
- 调用 Service Worker 的 fetch 事件响应
- 查看 memory cache
- 查看 disk cache,这里细分为:
- 有强缓存且未失效,则是用强缓存,不请求服务器,返回的状态码都是 200
- 有强缓存且已失效,使用协商缓存判断,是返回 304 或者是 200
4.缓存策略的实际使用场景
对于不常变化的资源:
Cache-Control: max-age=31536000
通常给 Cache-Control 设置一个很大的值(比如一年),为了解决更新问题,需要在文件上添加一个 hash,这样就达到了更改引用 URL 的目的。
对于经常变化的资源:
Cache-Control: no-cache
我们可以不使用强缓存,每次都向浏览器发送请求,然后配合 ETag 或者 Last-Modified 来验证资源缓存是否有效。
5.浏览器的本地存储
cookie、WebStorage(sessionStorage、localStorage)、IndexDB
(1)cookie
HTTP是无状态协议,对之前的请求和响应状态没有记忆,所以引入状态管理技术把请求和响应的信息状态保存下来。
客户端用Cookie,服务端用Session,Session依赖Cookie。
Cookie使用max-age和expires设置过期时间,通过键值对的形式,可以设置多个。
(max-ageExpires在HTTP/1.0中已经定义,Cache-Control:max-age在HTTP/1.1中才有定义,为了向下兼容,仅使用max-age不够;
- Expires指定一个绝对的过期时间(GMT格式),这么做会导致至少2个问题:
1)客户端和服务器时间不同步导致Expires的配置出现问题;
2)很容易在配置后忘记具体的过期时间,导致过期来临出现浪涌现象; - max-age 指定的是从文档被访问后的存活时间,这个时间是个相对值(比如:3600s),相对的是文档第一次被请求时服务器记录的Request_time(请求时间)
- Expires指定的时间可以是相对文件的最后访问时间(Atime)或者修改时间(MTime),而max-age相对对的是文档的请求时间(Atime)
- 在Apache中,max-age是根据Expires的时间来计算出来的max-age = expires- request_time:(mod_expires.c))
Cookie机制
session机制:
当服务器收到请求需要创建session对象时,首先会检查客户端请求中是否包含sessionid。如果有sessionid,服务器将根据该id返回对应session对象。如果客户端请求中没有sessionid,服务器会创建新的session对象,并把sessionid在本次响应中返回给客户端。通常使用cookie方式存储sessionid到客户端,在交互中浏览器按照规则将sessionid发送给服务器。如果用户禁用cookie,则要使用URL重写,可以通过response.encodeURL(url) 进行实现;API对encodeURL的结束为,当浏览器支持Cookie时,url不做任何处理;当浏览器不支持Cookie的时候,将会重写URL将SessionID拼接到访问地址后。
| Cookie | Session | |
|---|---|---|
| 保存地址 | 客户端 | 服务端 |
| 声明周期 | 可以自己设置,默认到浏览器关闭 | 保存一定时间 |
| 存储内容 | 文本字符串 | 对象 |
| 存储大小 | <4KB | 没有限制 |
| 安全性 | 弱 | 强 |
| 应用场景 | 保存网站登录信息; 保存上次查看的页面;浏览计数 | 网上商城的购物车; 保存用户登陆信息;防止用户非法登陆。 |
(2)WebStorage
WebStorage的目的是解决通过客户端存储不需要频繁发送回服务器的数据时使用Cookie的问题,把数据保存在本地。
WebStorage
Storage类型用于保存键/值对数据,与其他对象一样,增加了以下方法:
- clear():删除所有值;
- getItem():取得给定name的值;
- key(index):取得给定数值位置的值;
- removeItem():删除给定name的数据;
- setItem():设置给定的name值
| sessionStorage | localStorage | |
|---|---|---|
| 生命周期 | 页面关闭就结束 | 除非自己删除,否则一直存在。 |
| 大小 | 5MB | 5Mb |
| 保存地址 | 客户端 | 客户端 |
| 易用性 | 直接调用接口 | 直接调用接口 |
区别就在于声明周期,一个是会话级别存储,一个是持久化存储。
(3)IndexDB
IndexedDB是运行在浏览器中的非关系型数据库, 本质上是数据库,
理论上这个容量是没有上限的。
重要特性:
- 键值对存储。内部采用对象仓库存放数据,数据采用键值对的方式来存储。
- 异步操作。数据库的读写属于 I/O 操作, 浏览器中对异步 I/O 提供了支持。
- 受同源策略限制,即无法访问跨域的数据库。