一.浏览器缓存背景
由于http是一种无状态的协议,用户在浏览器上的每一次行为都将发送到服务器,而服务器却没法记住跟踪和记录这些行为,以至于相同的行为也会触发请求,这给网络通信带来了极大的性能开销。
HTTP Cookie(也叫Web Cookie或浏览器Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie使基于无状态的HTTP协议记录稳定的状态信息成为了可能。
Cookie曾一度用于客户端数据的存储,因当时并没有其它合适的存储办法而作为唯一的存储手段,但现在随着现代浏览器开始支持各种各样的存储方式,Cookie渐渐被淘汰。由于服务器指定Cookie后,浏览器的每次请求都会携带Cookie数据,会带来额外的性能开销(尤其是在移动环境下)。新的浏览器API已经允许开发者直接将数据存储到本地,如使用 Web storage API (LocalStorage和SeeionStorage)或 IndexedDB
二.缓存实现方式
1.Cookie
cookie 的作用就好比服务器给你贴个标签,然后你每次向服务器再发请求时,服务器就能够 cookie 认出你。
cookie 存储在客户端: cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。
适用场景
- 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
- 个性化设置(如用户自定义设置、主题等)
- 浏览器行为跟踪(如跟踪分析用户行为等)
常见用法
(1)http response header中的set-cookie
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
(2)在js中通过document.cookie可以读写cookie,以键值对的形式展示
//封装一个cookie对象
var manageCookie={
setCookie:function(name,value,time){ //设置cookie的函数
document.cookie=name+'='+value+';max-age='+time;
return this; //返回对象方便链式调用
},
removeCookie:function (name){ //移除cookie的函数
return this.setCookie(name,'',-1);
},
getCookie:function(name,callback){
var allCookieArr=document.cookie.split('; '); //把每一条cookie分割成数组,分号后面有个空格
console.log(document.cookie);
var len=allCookieArr.length;
for(var i=0;i<len;i++){
var itemCookieArr=allCookieArr[i].split('='); //一条里面的name和值分开
if(itemCookieArr[0]==name){
callback(itemCookieArr[1]);
return this;
}
}
// callback(undefined);
return this;
}
}
manageCookie
.setCookie('color','red','10000')
.setCookie('nihao','buhao','10000')
.getCookie('nihao',aa);
function aa(data){
console.log(data);
}
劣势
- cookie容量小
比如说电商网站的购物车功能,信息量大,而且结构也比较复杂,无法通过简单的 cookie 机制传递这么多信息,而且要知道 cookie 字段是存储在 HTTP header 中的,就算能够承载这些信息,也会消耗很多的带宽,比较消耗网络资源。
安全
- 会话劫持和XSS:
在Web应用中,Cookie常用来标记用户或授权会话。因此,如果Web应用的Cookie被窃取,可能导致授权用户的会话受到攻击。常用的窃取Cookie的方法有利用社会工程学攻击和利用应用程序漏洞进行XSS攻击。
HttpOnly类型的Cookie由于阻止了JavaScript对其的访问性而能在一定程度上缓解此类攻击。 如:
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
- 跨站请求伪造(CSRF)
维基百科已经给了一个比较好的CSRF例子。比如在不安全聊天室或论坛上的一张图片,它实际上是一个给你银行服务器发送提现的请求.有兴趣请移步探索
2.session
session 是一个数据结构,是另一种记录服务器和客户端会话状态的机制,是基于 cookie 实现的,session 存储在服务器端,sessionId 会被存储到客户端的cookie 中。
由网站的开发者设计,所以可以承载各种数据,只要客户端的 cookie 传来一个唯一的 session ID,服务器就可以找到对应的 session,认出这个客户。
session 就可以配合 cookie 解决容量小的,比如说一个 cookie 存储这样一个变量sessionID=xxxx,仅仅把这一个 cookie 传给服务器,然后服务器通过这个 ID 找到对应的 session,这个 session 是一个数据结构,里面存储着该用户的购物车等详细信息,服务器可以通过这些信息返回该用户的定制化网页,有效解决了追踪用户的问题
劣势
-
Session 保存的东西越多,就越占用服务器内存
对于用户在线人数较多的服务器的内存压力就会比较大,所以 session 一般都会有一个过期时间,服务器一般会定期检查并删除过期的 session,如果后来该用户再次访问服务器,可能就会面临重新登录等等措施。 -
依赖于cookie(sessionID保存在cookie),如果禁用cookie,则要使用url重写,不安全
-
创建Session变量有很大的随意性,可随时调用,不需要开发者做精确地处理,所以,过度的使用session变量将会导致代码不可读而且不好。
3.LocalStorage
只读的localStorage 属性允许你访问一个Document 源(origin)的对象 Storage;存储的数据将保存在浏览器会话中。localStorage 类似 sessionStorage,但其区别在于:存储在 localStorage 的数据可以长期保留;而当页面会话结束——也就是说,当页面被关闭时,存储在 sessionStorage 的数据会被清除 。
特点
- 保存的数据长期存在,下一次访问该网站的时候,网页可以直接读取以前保存的数据。
- 大小为5M左右
- 仅在客户端使用,不和服务端进行通信
- 接口封装较好
适用场景
一般用于在前端缓存一些数据时使用,这些数据一般是只在前端使用,而后端不使用的,所以不用每次都往后端发送。(或是前端做统计,后端只要一个统计结果之类的)
比如一些网站提供的编辑器,自带草稿功能,每隔几秒钟或几分钟自动保存当前编辑的内容,刷新页面,或是把浏览器关掉重新打开编辑页面可以自动恢复之前编辑的内容的。这种信息就适合存放在 localStorage 中。
常见用法
下面的代码片段访问了当前域名下的本地 Storage 对象,并通过 Storage.setItem() 增加了一个数据项目。
localStorage.setItem('myCat', 'Tom');
//该语法用于读取 localStorage 项,如下:
let cat = localStorage.getItem('myCat');
//该语法用于移除 localStorage 项,如下:
localStorage.removeItem('myCat');
该语法用于移除所有的 localStorage 项,如下:
localStorage.clear();
3.sessionStorage
sessionStorage 属性允许你访问一个,对应当前源的 session Storage 对象。它与 localStorage 相似,不同之处在于 localStorage 里面存储的数据没有过期时间设置,而存储在 sessionStorage 里面的数据在页面会话结束时会被清除。
特点
- 页面会话在浏览器打开期间一直保持,并且重新加载或恢复页面仍会保持原来的页面会话。
- 在新标签或窗口打开一个页面时会复制顶级浏览
- 会话的上下文作为新会话的上下文,这点和 session cookies 的运行方式不同。
- 打开多个相同的URL的Tabs页面,会创建各自的sessionStorage。
- 关闭对应浏览器tab,会清除对应的sessionStorage。
- 大小为5M左右
- 仅在客户端使用,不和服务端进行通信
适用场景
sessionStorage 更适合用来存储生命周期和它同步的会话级别的信息。这些信息只适用于当前会话,当你开启新的会话时,它也需要相应的更新或释放。比如微博的 sessionStorage就主要是存储你本次所在会话的记录(以专题来区分)
lasturl 对应的就是你上一次访问热门这个路由的 URL 地址,这个地址是即时的。当你切换 URL 时,它随之更新,如从热门切换到头条其值发生改变,关闭该页面时会清除对应的sessionStorage。
常见用法
下面的示例会自动保存一个文本输入框的内容,如果浏览器因偶然因素被刷新了,文本输入框里面的内容会被恢复,因此写入的内容不会丢失。
// 获取文本输入框
let field = document.getElementById("field");
// 检测是否存在 autosave 键值
// (这个会在页面偶然被刷新的情况下存在)
if (sessionStorage.getItem("autosave")) {
// 恢复文本输入框的内容
field.value = sessionStorage.getItem("autosave");
}
// 监听文本输入框的 change 事件
field.addEventListener("change", function() {
// 保存结果到 sessionStorage 对象中
sessionStorage.setItem("autosave", field.value);
});
4.inedexDB
IndexedDB是一种底层API,用于客户端存储大量结构化数据(包括, 文件/ 二进制大型对象(blobs)。该API使用索引来实现对该数据的高性能搜索。虽然 Web Storage 对于存储较少量的数据很有用,但对于存储更大量的结构化数据来说,这种方法不太好用。IndexedDB提供了一个解决方案,理论上来说,IndexedDB 是没有存储上限的(一般来说不会小于 250M)
特点
-
同源策略
正如大多数的web储存解决方案一样,IndexedDB也遵守同源策略。 因此当你在某个域名下操作储存数据的时候,你不能操作其他域名下的数据。 -
同步和异步
使用IndexedDB执行的操作是异步执行的,以免阻塞应用程序。IndexedDB最初包括同步和异步API。同步API仅用于Web Workers已从规范中移除,因为尚不清晰是否需要。但如果Web开发人员有足够的需求,则可以重新引入同步API。 -
储存限制和回收标准
许多Web技术在客户端(即本地磁盘)存储各种数据。IndexedDB是最常见的一个。浏览器计算分配给Web数据存储的空间以及达到该限制时要删除的内容的过程并不简单,并且在浏览器之间有所不同。 -
支持事务
IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况
常见用法
见实例,如何使在indexedDB中存储图像和文件:
https://hacks.mozilla.org/2012/02/saving-images-and-files-in-localstorage/
三.缓存实现对比
缓存方式 | 存储地址 | 存储方式和大小 | 生命周期 | 作用域 | 服务通信 |
---|---|---|---|---|---|
cookie | 客户端 | 字符串或文本形式(<=4kb) | 客户端请求服务端生成,可设置失效小时间,若未设置或则仅在客户端生成,即默认关闭客户端后失效。 | Domain 和 Path 标识定义了Cookie的*作用域:*即Cookie应该发送给哪些URL。Domain 标识指定了哪些主机可以接受Cookie。 如果不指定,默认为当前文档的主机(不包含子域名)。如果指定了Domain ,则一般包含子域名。 | 每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题 |
session | 服务端 | 任何类型的对象(大小无限制) | session在服务端是可以设置失效时间的,如果客户端在该时段内无活跃访问,session将失效。客户端存储的sessionID同cookie生命周期 | 同cookie | 参与同cookie |
LocalStorage | 客户端 | 只能存储字符串类型,复杂对象用ES6的JSON对象的stringify和parse处理(<=5Mb) | 除非主动清除,否则永久保存 | 限定在文档源级别的。文档源通过协议、主机名以及端口三者(必须同源)来确定。 | 不参与 |
sessionStorage | 客户端 | 只能存储字符串类型,复杂对象用ES6的JSON对象的stringify和parse处理(<=5Mb) | 仅在当前会话有效,关闭页面或浏览器后被清除 | 更严苛一点,限定在文档源级别(同源)。还要求在同一窗口(也就是浏览器的标签页)下,否则sessionStorage的数据是无法共享的。 | 不参与 |
indexedDB | 客户端 | 任何类型的对象(大小无限制) | 除非主动清除,否则永久保存 | 限制在包含它们的文档源中,每一个数据库对应创建该数据库的域名,两个同源的Web页面互相之间可以访问对方的数据,但非同源的页面就不行 | 不参与 |