客户端缓存分为Http缓存和本地缓存,使用缓存好处很多,例如减少相同数据的重复传输,节省网络带宽资源缓解网络瓶颈,降低了对原始服务器的要求,避免出现过载,这样服务器可以更快响应其他的请求
Http缓存http缓存分为强制缓存和协商缓存,主要用来在客户端存储一些不经常变化的的静态文件,像图片、CSS、JS等。在讲强制缓存和协商缓存之前,先了解一下Http缓存的过程:
强制缓存浏览器在请求某一个资源时,会先获取资源的header信息,判断是否命中强缓存(Cache-Control和expires信息),若命中,则直接从缓存中获取资源信息,包括header信息,本次请求就不会与服务器通信。
缓存方案
Expires:Expires(/ɪkˈspaɪərz/),到期的意思,response header里的过期时间,浏览器再次加载资源时,如果在这个过期时间内,则命中强缓存。
Cache-Control:当值设为max-age=300时,则代表在这个请求正确返回时间(浏览器也会记录下来)的5分钟内再次加载资源,就会命中强缓存。
Cache-Control与Expires的作用一致,都是指明当前资源的有效期,控制浏览器是否直接从浏览器缓存取数据还是重新发请求到服务器取数据。只不过Cache-Control的选择更多,设置更细致,如果同时设置的话,其优先级高于Expires
cache-control除了该字段外,还有下面几个比较常用的设置值:
public、private、no-cache、no- store、no-transform、must-revalidate、proxy-revalidate、max-age等等
各个消息中的指令含义如下:
Public指示响应可被任何缓存区缓存。
Private指示对于单个用户的整个或部分响应消息,不能被共享缓存处理。这允许服务器仅仅描述当用户的部分响应消息,此响应消息对于其他用户的请求无效。
no-cache指示请求或响应消息不能缓存
no-store用于防止重要的信息被无意地发布。在请求消息中发送将使得请求和响应消息都不使用缓存。
max-age指示客户机可以接收生存期不大于指定时间(以秒为单位)的响应。
min-fresh指示客户机可以接收响应时间小于当前时间加上指定时间的响应。
max-stale指示客户机可以接收超出超时期间的响应消息。如果指定max-stale消息的值,那么客户机可以接收超出超时期指定值之内的响应消息。
协商缓存如果没有命中强缓存,浏览器会发送请求到服务器,请求会携带第一次返回的有关缓存的header字段信息(Last-Modifued/If-Modified-Since和Etag/If-None-Match),由服务器根据header信息来比对结果是否协商缓存命中。若命中,则服务器返回新的响应header信息更新缓存中的对应header信息,但是不返回资源内容,它会告知浏览器可以直接从缓存获取;否则返回最新的资源内容。
缓存方案
Last-Modify/If-Modify-Since:浏览器第一次请求一个资源的时候,服务器返回的header中会加上Last-Modify,Last-modify是一个时间标识该资源的最后修改时间;当浏览器再次请求该资源时,request的请求头中会包含If-Modify-Since,该值为缓存之前返回的Last-Modify。服务器收到If-Modify-Since后,根据资源的最后修改时间判断是否命中缓存
Etag/If-None-Match:web服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(etag)(生成规则由服务器决定)。当资源过期时(使用Cache-Control标识的max-age),发现资源具有Etag声明,则再次向web服务器请求时带上头If-None-Match (Etag的值)。web服务器收到请求后发现有头If-None-Match 则与被请求资源的相应校验串进行比对,决定是否命中协商缓存;
Last-Modified和Etag区别
Last-Modified和Etag的区别 在描述中对于Last-Modified和Etag的使用可以说是差不多的,但是,Etag(http1.1)的出现是为了解决几个Last-Modified比较难解决的问题:
- 一些文件也许会周期性地更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新GET
- 某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),If-Modified-Since能检查到的粒度是s级的,这种修改无法判断(或者说UNIX记录MTIME只能精确到秒)
- 某些服务器不能精确地得到文件的最后修改时间 这时,利用Etag能够更加准确地控制缓存,因为Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符
Last-Modified与ETag是可以一起使用的,服务器会优先验证ETag,一致的情况下,才会继续比对Last-Modified,最后才决定是否返回304。
强缓存与协商缓存的区别
获取资源方式 | 状态码 | 是否发送到服务器 | |
强缓存 | 缓存中获取 | 200(from cache) | 否,直接从缓存中获取 |
协商缓存 | 缓存中获取 | 304(not modify) | 是,通过服务器来告知缓存是否可用 |
本地缓存和浏览器有很大关系,可能会出现不同的浏览器使用的缓存技术不同,常见的本地缓存:
cookie- 1.兼容所有的浏览器
- 2.有存储的大小限制,一般一个源(一个域下)只能存储4KB内容
- 3.cookie有过期时间(当然我们自己可以手动设置这个时间)
- 4.*毒软件或者浏览器的垃圾清理都可能会把cookie信息强制清除掉
- 5.在隐私或者无痕浏览模式下,是不记录cookie的
- 6.cookie不是严格的本地存储,因为要和服务器之间来回传输
Chrome浏览器缓存的Cookie缓存
localStorage
- 1.不兼容IE8及以下
- 2.也有存储的大小限制,一个源下最多只能存储5MB左右
- 3.本地永久存储,只要你不手动删除,永远存储在本地(但是我们可以基于API removeItem/clear手动清除一些自己想要删除的信息)
- 4.*毒软件或者浏览器的垃圾清理暂时不会清除localStorage(新版本谷歌浏览器会清除localStorage等信息)
- 5.在隐私或者无痕浏览模式下,是记录localStorage的
- 6.localStorage和服务器没有半毛钱关系
- sessionStorage 和localStorage 唯一的区别在于sessionStorage 是临时存储,只对当前回话有效,当浏览器当前标签页关闭则失效,与localStorage 拥有同样的方法。
localStorageh和sessionStorage 都只拥有大约5M的存储空间,不适用于存储大数据量数据。对于数据量较大的数据缓存,我们应该应用本地数据库实现(indexDB)
indexDBIndexedDB 具有以下特点:
(1)键值对储存。
IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以"键值对"的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。
(2)异步。
IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。
(3)支持事务。
IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。
(4)同源限制
IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。
(5)储存空间大
IndexedDB 的储存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有上限。
(6)支持二进制储存。
IndexedDB 不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象)。
缓存强制刷新在强制刷新的时候浏览器就不再发送IF-Modified-Since了,而会带上
from disk cache & from memory cache
可以验证请求是否使用了浏览器缓存和是否发送请求给服务器端。
当点击链接、引入外部资源和浏览器的前进后退的时候。
from memory cache
字面理解是从内存中,其实也是字面的含义,这个资源是直接从内存中拿到的,不会请求服务器一般已经加载过该资源且缓存在了内存当中,当关闭该页面时,此资源就被内存释放掉了,再次重新打开相同页面时不会出现from memory cache的情况
from disk cache
是从磁盘当中取出的,也是在已经在之前的某个时间加载过该资源,不会请求服务器但是此资源不会随着该页面的关闭而释放掉,因为是存在硬盘当中的,下次打开仍会from disk cache
不做深入研究
js脚本,css,图片,音视频,字体
Age
是CDN添加的属性表示在CDN中缓存了多少秒
via
用来标识CDN缓存经历了哪些服务器,缓存是否命中,使用的协议
浏览器缓存原则- 首页可以看做是框架 应该禁用缓存,以保证加载的资源都是最新的
- 还有一些场景下我们希望禁用浏览器缓存。比如轮训api上报数据数据
- 浏览器缓存很难彻底禁用,大家的做法是加版本号,随机数等方法。
- 只缓存200响应头的数据,像3XX这类跳转的页面不需要缓存。
- 对于js,css这类可以缓存很久的数据,可以通过加版本号的方式更新内容
- 不需要强一致性的数据,可以缓存几秒
- 异步加载的接口数据,可以使用ETag来校验。
- 在服务器添加Server头,有利于排查错误
分为手机APP和Client以及是否遵循http协议,在没有联网的状态下可以展示数据,流量消耗过多
- 漂亮的加载过程
- 提前下发避免秒*时同时下发数据造成流量短时间暴增
- 兜底数据 在服务器崩溃和网络不可用的时候展示
- 临时缓存 退出即清理
- 固定缓存 展示框架这种,可能很长时间不会更新,可用随客户端下发
- 父子连 页面跳转时有一部分内容不需要重新加载,可用从父菜单带过来
- 预加载 某些逻辑可用判定用户接下来的操作,那么可用异步加载那些资源
- 异步加载 先展示框架,然后异步加载内容,避免主线程阻塞