概述
Cookie、LocalStorage、SessionStorage、IndexDB这些作为浏览器的存储入口,也是经典的八股文了,本文再次冷饭热吃来介绍这些API,主要是因为在其他文章中看到了一些个人感觉有用的小知识点,所以在这记录一下,以便有序复习浏览。不多逼逼,开始正文。
Cookie
HTTP Cookie(也叫 Web Cookie 或浏览器 Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据。浏览器会存储 cookie 并在下次向同一服务器再发起请求时携带并发送到服务器上。不同的请求方式对Cookie的处理不一样:
特性 | Fetch API (credentials ) |
XMLHttpRequest (withCredentials ) |
---|---|---|
默认值 | 'same-origin' |
false |
默认行为 | 同源请求中发送 Cookie;跨域不发送 | 不发送 Cookie(无论同源还是跨域) |
发送同源 Cookie | 默认发送 | 需要设置 withCredentials = true |
发送跨域 Cookie | 需要设置 credentials: 'include' |
需要设置 withCredentials = true |
不发送 Cookie | 设置 credentials: 'omit' |
默认行为 false |
由于HTTP 协议是无状态的,对于服务器无法区分与其连接的客服端信息,所以引入了Cookie用以记录稳定的状态信息。Cookie是基于HTTP的,通常用于告知服务端区分两个请求是否来自同一浏览器。
Cookie主要有以下特点:
- 这四种中唯一直接和服务器交互的存储方式
- 可以设置Cookie.SameSite来阻止CSRF攻击。
- 保存字符串类型的值,可通过序列号处理,大小为4KB
- 没有设置
Expires/Max-Age(过期时间/有效时间)
时,默认是会话式的,关闭窗口时会删除Cookie记录
SameSite 属性值 | 描述 | 适用场景 |
---|---|---|
Strict | Cookie 仅在同站点请求中发送,不会在任何第三方请求(如跨站点链接、表单提交、资源加载)中发送。 | 高安全性场景,防止所有跨站点请求携带 Cookie,如敏感数据的保护。 |
Lax(默认) | Cookie 在同站点请求中发送,并在部分跨站点请求(如通过链接导航的 GET 请求)中发送。 | 适用于大多数场景,平衡安全性和用户体验,防止大多数 CSRF 攻击。 |
None | Cookie 始终发送,包括所有跨站点请求,必须配合 Secure 属性(即仅通过 HTTPS 发送)。 |
需要跨站点访问 Cookie 的场景,如第三方嵌入内容或跨站登录。 |
CSRF(Cross-site request forgery)跨站请求伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。白话
就是利用你的Cookie来冒充你和服务器进行通信,进行不好的操作
。
WebStorage
LocalStorage、SessionStorage统称为Web Storage(大小5M),而现代存储API包括: Web Storage、IndexedDB。 由于每次请求都会携带Cookie,随着Cookie存储数据过多时,会造成不必要的性能消耗。随着现代存储API出现之后,Cookie一般用于保存登录信息和服务器交互,其他数据推荐使用现代存储方式。
LocalStorage、SessionStoraged是通过 Window.sessionStorage 和 Window.localStorage 属性使用(更确切的说,在支持的浏览器中 Window 对象实现了 WindowLocalStorage 和 WindowSessionStorage 对象并挂在其 localStorage 和 sessionStorage 属性下)------调用其中任一对象会创建 Storage 对象,通过 Storage 对象,可以设置、获取和移除数据项。对于每个源 sessionStorage 和 localStorage 使用不同的 Storage 对象------独立运行和控制。
两者区别如下
-
持久性:
- LocalStorage:数据持久化,直到手动删除。
- SessionStorage:数据在浏览器会话结束时删除,浏览器关闭即失效。
-
数据共享:
- LocalStorage:在同一源的所有标签页和窗口共享数据。
- SessionStorage:仅在同一窗口或标签页内共享数据,不跨标签页。
多个tab间能否共享SessionStorage?
可能有的同学看到这里会疑问为什么会有这个问题,上面介绍了SessionStorage是会话式的,以窗口/tab
进行分配空间,那在多个tab间肯定是不能共享的。
但是当那在当前页面打开控制台,然后进行以下操作:
- 在当前页面通过sessionStorage.setItem随意设置一个值,然后通过getItem获取,当然此时肯定会获取到设置的值。
- 然后继续在当前页面控制台输入
Window.open('http://www.baidu.com')
(任意地址都可以,本文以百度为例) - 在跳转的新页面通过getItem获取,此时你会发现能获取到。呀,这就和之前理解的不一样的,从这里看
sessionStorage
好像也能共享? - 然后在新的页面再次sessionStorage.setItem设置一个新值,并返回跳转前页面通过getItem访问,此时会返回
null
到这里可能有的人就有些迷惑了,要明白为什么会这样,首先要理解SessionStorage是会话式的
这句话的含义。我的理解上面通过在当前页面通过window.open新开一个页面的方式其实是当前页面会话的一个延续,此时延续的会话会复制
当前会话的内容。注意是复制而不是共享
这就是为什么在新的页面能访问而上一个页面无法访问的原因。同理不在控制台调整,在页面内通过<a>
标签也能实现。
举例说明:
- A页面点击超链接跳转到B页面,如果是target="_self",(默认就是_self),跳转是在同一个浏览器tab页面里面进行的,此时不管怎么操作session,在同一个tab里面两个页面的session都是一样的。
- 如果target="_blank",也得分为两种情况:rel表示当前页面和跳转的目标页面具有什么关系。
- 如果a标签没有rel="opener"属性,此时不会携带session。
- 如果a标签有rel="opener"属性,则适用前面的原则,即从A跳转到B是一个会话,B页面会复制A页面的 session,此时在B页面回退(因为是新tab打开,所以无法回退,只能是重新有一个a标签,点击之后打开新tab,同样适用前面A页面的点击跳转规则),即打开新的A页面A1,此时A1页面是复制的B页面的session,如果B页面的session是修改过的,那么A1就是复制的最新的B页面的session。
<a>
标签的rel
属性指定了目标对象到链接对象的关系。而值为opener
则创建一个辅助浏览上下文,可以理解为当前会话的延续。详细值可查看MDN
一句话总结:SessionStorage是会话性质的,同一个会话/会话的延续会复制session。
Web Storage是同步还是异步的?
我们知道LocalStorage保存在磁盘中,SessionStorage保存在内存中。通常情况下磁盘等IO设备为了不阻塞进程,都是异步IO的。那这么看来LocalStorage就是异步的了?
其实不是,在控制台执行下面代码:sessionLocalstorage同理都是同步的
javascript
const testLocalStorage = () => {
console.log("==========> 设置localStorage之前");
localStorage.setItem('testLocalStorage', '我是同步的');
console.log("==========> 获取localStorage之前");
console.log('=========获取localStorage', localStorage.getItem('testLocalStorage'))
console.log("==========> 获取localStorage之后");
}
testLocalStorage()
可以看到输入是同步的,那这是为什么呢?
原因就是因为Js是单线程的,当通过js代码在访问 localStorage 时,浏览器提供的API接口通常会处于js执行线程上下文中直接调用。这意味着尽管硬盘是IO设备,当一个js执行流程访问 localStorage 时,它将同步地等待数据读取或写入完成,该过程中js执行线程会阻塞。
完整的流程
localStorage 实现同步存储的方式就是阻塞 JavaScript 的执行,直到数据的读取或者写入操作完成。这种同步操作的实现可以简单概述如下:
- js线程调用: 当 JavaScript 代码执行一个 localStorage 的操作,比如 localStorage.getItem('key') 或 localStorage.setItem('key', 'value'),这个调用发生在 js 的单个线程上。
- 浏览器引擎处理: 浏览器的 js 引擎接收到调用请求后,会向浏览器的存储子系统发出同步IO请求。此时 js 引擎等待IO操作的完成。
- 文件系统的同步IO: 浏览器存储子系统对硬盘执行实际的存储或检索操作。尽管操作系统层面可能对文件访问进行缓存或优化,但从浏览器的角度看,它会进行一个同步的文件系统操作,直到这个操作返回结果。
- 操作完成返回: 一旦IO操作完成,数据要么被写入硬盘,要么被从硬盘读取出来,浏览器存储子系统会将结果返回给 js 引擎。
- JavaScript线程继续执行: js 引擎在接收到操作完成的信号后,才会继续执行下一条 js 代码。
在同步的 localStorage 操作期间,由于 js 的单线程性质,整个线程会阻塞,即不会执行其他任何js代码,也不会进行任何渲染操作,直到 localStorage 调用返回。所以一般不要将大量数据保存在Storage中,避免长时间同步阻塞,影响体验
。
Web Storage有大小限制是因为同步阻塞吗?
主要原因如下:
- 资源公平分享:同一用户可能会访问大量不同的网站,如果没有限制,随着时间的积累,每个网站可能会消耗大量的本地存储资源。这样会导致本地存储空间被少数几个站点占用,影响到用户访问其他网页的体验。限制大小可以确保所有网站都有公平的存储机会。
- 防止滥用:如果没有存储限制,网站可能会滥用 localStorage,存储大量数据在用户的设备上,这可能导致设备存储空间迅速耗尽,也可能侵犯用户的隐私。
- 性能限制:如之前提到的,localStorage 的操作是阻塞的。如果网站能够存储大量数据,就会加剧读写操作对页面性能的影响。
- 存储效率:localStorage 存储的是字符串形式的数据,不是为存储大量或结构化数据设计的。当尝试存储过多数据时,效率会降低。
- 历史和兼容性:5MB 的限制很早就已经被大多数浏览器实现,并被作为一个非正式的标准被采纳。尽管现在有些浏览器支持更大的 localStorage,但出于跨浏览器兼容性的考虑,开发者通常会假设这个限制。
- 浏览器政策:浏览器厂商可能会依据自己的政策来设定限制,可能是出于提供用户更一致体验的角度,或者是出于管理用户数据的方便。
总结
本文主要介绍了Cookie、LocalStorage、SessionStorage三种存储机制,总结一下就是: 三者都只能保存字符串数据,并且都有大小限制。其中Cookie一般用于鉴权,LocalStorage将数据保存在磁盘中可以用于跨tab以及持久化访问,SessionStorage将数据保存在内存中,是会话式的,当在同一个会话或者会话延续时,可以访问Session。LocalStorage是共享,SessionStorage是复制。