网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
目录
一、web存储概念简介
1. 什么是web存储?
web存储指的是在web通信过程中,由客户端(如浏览器)对少量数据进行的本地存储(注:本文所说的是广义的web存储,而不单指Web Storage)。web存储类型主要包括古老的cookie,以及HTML5推出的sessionStorage、localStorage和indexedDB等。
web存储的宿主环境通常是浏览器,借助浏览器提供的JavaScript接口,我们可以实现对这些数据的增删改查。
2. 为什么需要web存储?
这要从最古老的的cookie说起。
cookie的诞生源于这样一个矛盾:http协议是无状态的,而服务端却有区分不同客户端的实际需要。
先来理解http协议的无状态性。无状态性指的是,http协议对任意两次数据传输是没有记忆能力的。举个例子,假如某个客户端先向服务端发送了一条消息,随后又向服务端发送了另一条消息,那么服务器无法知道这两条消息来自同一个客户端。
之所以要将http协议设计为无状态的,是因为要维护所有客户端的连接状态对服务端的性能消耗很大,这会严重限制服务器的并发能力。并且在大多数情况下,维护这个状态并不能带来什么收益。所以http协议的设计者索性不对状态进行维护。
但在很多情况下,服务端必须区分不同的客户端。拿淘宝来说,在收到下单请求时,服务器必须知道是哪个用户在下单,这就要求它必须知道当前的下单请求和之前的哪个登录请求来自同一个客户端(否则该客户端每次发请求都必须把自己的账号信息和所有参数带上)。
为此,服务端设计了session(会话)机制。一个用户登录之后,服务端会为该用户创建一个会话对象,存储与该用户相关的参数(如用户名、登录时间等),这个对象直到用户退出登录或登录超时才会被销毁。每个会话对象有一个唯一的id,客户端只要在随后发送的请求中携带上这个id,服务端就能确定是哪个用户发送的请求。
这样,客户端必须有一种存储这个id的机制,来保证其在退出登录前的每个请求中都可以携带这个id。于是,cookie诞生了。
用户登录时,服务端会为该用户创建一个会话(session)对象,然后服务端把会话对象的id写入响应参数中发送给客户端。客户端收到响应后,取出该参数,保存在浏览器内。随后每次发送请求时都携带上这个id,服务端根据这个id,就可以确定该请求来自于哪个用户。这个用于存储id的特殊字段,就称为cookie(当然,cookie不仅仅可以存储session的id,还可以存储其他参数)。
所以说,cookie的基本作用就是携带用户标识,帮助服务端区分不同的客户端。
cookie之后的几类存储:sessionStorage、localStorage和indexedDB出现的原因就很简单了:将更多的会话数据保存在浏览器端,可以减少网络传输,提升网站性能(虽然cookie也可以这样使用,但它的单个容量只有4KB,所以不适合这类用途)。
二、web存储详解
1. cookie
上面我们已经对cookie的来源进行了详细的介绍,这里要介绍的是cookie的用法及特点。
HTTP Cookie简称cookie,是在HTTP请求发送Set-Cookie HTTP头作为响应的一部分,通过name=value的形式存储的字符串。前端收到该响应后,会将cookie保存在浏览器内,并在每次的用户请求中携带该字符串。
cookie在前端可以通过document.cookie获得,如:
> document.cookie < "\_ga=77911531582348967; \_gid=10966846041582958782"
上面的cookie里共存储了两个参数:_ga和_gid,两个参数由一个分号和一个空格隔开。cookie本身是用encodeURIComponent编码的,因此,如果cookie中包含中文,需要用对应的decodeURIComponent解码才能得到对应值。
除了key和value外,每个参数还会携带其他几个属性,包括域、路径、失效时间、安全标志等,这些参数在使用document.cookie输出时无法看到,不过可以在调试工具中查看:
上面几个参数的含义分别是:
- key,键名。
- value,键值。
- Domain,cookie的域,即该网站所在的域,需要注意的是,cookie的域是不区分端口号的。
- Path,cookie的存储路径,默认是当前项目的根路径。
- Expires/Max-Age,cookie的失效时间,以毫秒为单位。不设置时默认在session失效时失效。
- Size,cookie的大小。
- HttpOnly,是否只能通过http请求传输。某些重要的cookie(如sessionId),只能由后台来设置,并且是不能随意修改的,就可以设置该标志位。
- Secure,安全标志。设置了该参数后,该cookie只在https请求中才会被携带。
- SameSite,为防止CSRF攻击和用户追踪而新增的安全属性,具体可参考阮一峰:Cookie 的 SameSite 属性。
一般来说,cookie只用于保存与登录相关的信息,其中最重要的是服务端会话(session)对象的id。它不适合存储大量的数据,因为浏览器限制了每个cookie的大小不能超过4KB。此外,浏览器对单个域下cookie的数量也是有限制的,该数量因浏览器而异,对开发者来说,这个数量一般不要超过20个。
cookie默认会在会话结束时失效,也可以手动修改max-age
或Expires
参数来设置cookie的失效时间,前者是规定cookie在多少秒后失效,而后者则是规定cookie在某个时间点失效。如果同时设置了这两个参数,则max-age优先级更高。
向cookie添加参数很简单:
document.cookie = "name=oeschger"; document.cookie = "favorite=tripe;max-age=120;path=/"; alert(document.cookie); // 显示: name=oeschger; favorite=tripe
注意,我们为favorite设置的其他参数虽然没有通过document.cookie打印出来,但它们仍然是有效的。比如上面的name会在服务端的session销毁时失效,而favorite则会在120秒后失效。
cookie没有提供直接删除参数的接口,但是可以通过将参数的max-age属性设置为0或者将Expires设置为当前时间,来使其立即失效,以达到删除cookie的目的,如:
document.cookie = "favorite=; max-age=0;";
这样就可以从cookie中删除favorite。
cookie作为客户端存储,有一个很大的优点,就是不受服务端架构的影响。我们知道,在分布式架构下,session很难在各个服务器之间共享。而cookie则完全不存在这个问题,它存储于客户端,可以被发送到任意一台服务器。
同时,cookie也存在几个很明显的缺点。一个是安全问题,cookie中存储了客户端的身份认证信息,如果被窃取,就可能产生不可预估的损失。另一个是上面提到的容量问题,这使得cookie不能广泛用于web存储。此外,cookie对通信带来的性能损耗也是不可忽视的。因为cookie中的所有参数会在每次向后端发送请求时被携带,如果cookie很大,就会导致每次的http请求体积变大,影响网站的响应速度。综上所述,cookie仅用于保存极少量的用户身份认证信息,并且需要通过加密策略保证cookie传输安全。
2. sessionStorage和localStorage
为什么要把这两个放在一起介绍呢?
因为它们都是继承自Storage
,原理和语法上都有极高的相似性,以至于网上有大量关于两者异同的介绍文章。先来看看它们的相同点:
(1). 相同点
1.从原理上来说,两者都属于浏览器端存储。前者称为“会话存储”,后者称为“本地存储”。它们都被部署在window对象上,因此可以通过下面的方式访问:
window.localStorage // 或者直接访问localStorage window.sessionStorage // 或者直接访问sessionStorage
2.两者的操作语法是一致的(这里以localStorage为例):
localStorage.setItem("name", "carter"); // 设置name: carter localStorage.age = "24"; // 设置age: 24 localStorage.getItem("name"); // 获取name的值carter localStorage.removeItem("name"); // 删除name localStorage.clear(); // 清空localStorage
sessionStorage的语法同上。
3.两者都遵循同源策略。即该存储只在同一个域下可以共享,跨域无法访问,这样可以保证数据的安全性。
4.localStorage和sessionStorage各自拥有5MB的存储空间,并且只能保存字符串类型的数据。对于非字符串类型的数据,一般需要使用JSON.stringify
方法压缩成字符串,使用时再用JSON.parse
进行解析。
(2). 不同点
1.两者的失效时间是不同的。
localStorage本身是不会失效的,即使关闭浏览器,下次再访问该网站仍然有效。localStorage没有提供直接设置失效时间的方法,我们需要使用一种特殊的策略来定期清除localStorage:
localStorage.setItem("name", JSON.stringify({ value: "carter", time: (new Date()).getTime() // 保存时间戳 })) function getItem(key, maxAge){ let obj = localStorage.getItem(key); if(obj){ // 获取变量值对象 obj = JSON.parse(obj); } // 存储时间小于最大生命周期时才读取该参数,否则将其清除 if((new Date()).getTime() - obj.time < maxAge){ return obj.value; } else { localStorage.removeItem(key); return ""; } } getItem("name", 60 \* 60 \* 1000); // 失效时间为1小时
简单来说,就是把保存localStorage的时间一同存储进去,取值的时候再手动判断是否超时。
但sessionStorage与页面会话是绑定的,当某个页面会话失效时,对应的sessionStorage就会被清除。需要注意的是,sessionStorage只在会话页面失效时才会失效,也就是说刷新页面或者通过某种方式恢复当前页面时(如点击浏览器的回退按钮,或使用浏览器的页面恢复功能),sessionStorage并不会失效,换句话说,当某个页面创建了sessionStorage之后,它就总是与其创建的sessionStorage共存亡。
2. 两者的有效范围不同。
在不跨域的情况下,localStorage可以跨页签生效,而sessionStorage仅在当前页签范围内有效。
这就是说,如果你新打开了一个与当前页面同域的页面,这个新页面会与当前页面共享localStorage,却不能共享sessionStorage。而如果你是在当前页面的iframe内打开的,并且没有跨域,那么localStorage和sessionStorage都是可以共享的。
3. indexedDB
尽管5M的空间已经相对较大了,但仍然无法满足所有的前端存储需求。
这是因为前端数据缓存对web站点的性能提升是巨大的(它可以有效减少http数据传输量,而这通常是导致网站卡顿的最主要因素),因此越来越多的站点倾向于在前端存储更多的数据。localStorage和sessionStorage无法满足这样的需求,因为一方面,它们的容量只有5M大小;另一方面,由于是非结构化存储,当数据量较大时,它们的操作速度不够快。
为此,HTML5规范推出了前端的事务型数据库indexedDB。它可以存储大量的结构化数据,具有几乎可以媲美后端数据库的读写性能(当然,从功能和容量上远不及后端数据库)。
使用indexedDB大概需要以下几个流程:
- 打开数据库:
var request = window.indexedDB.open(databaseName, version);
这样就可以打开或新建一个indexedDB数据库。当所传入的数据库名不存在时,就会新建一个数据库,否则将打开已有数据库。当省略了数据库版本号时,如果数据库已存在,则默认为当前版本,否则版本号为1
。
- 注册处理事件
调用open方法后,返回的是一个IDBRequest
对象,通过向其注册error
、success
和upgradenedded
事件,可以处理打开数据的结果。
error
表示数据库打开失败:
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
15886988469)]
[外链图片转存中…(img-Kak4ouRI-1715886988469)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!