Cookie、Web Storage介绍

概述

Cookie、LocalStorage、SessionStorage、IndexDB这些作为浏览器的存储入口,也是经典的八股文了,本文再次冷饭热吃来介绍这些API,主要是因为在其他文章中看到了一些个人感觉有用的小知识点,所以在这记录一下,以便有序复习浏览。不多逼逼,开始正文。

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 对象------独立运行和控制。

两者区别如下

  1. 持久性

    • LocalStorage:数据持久化,直到手动删除。
    • SessionStorage:数据在浏览器会话结束时删除,浏览器关闭即失效。
  2. 数据共享

    • LocalStorage:在同一源的所有标签页和窗口共享数据。
    • SessionStorage:仅在同一窗口或标签页内共享数据,不跨标签页。

多个tab间能否共享SessionStorage?

可能有的同学看到这里会疑问为什么会有这个问题,上面介绍了SessionStorage是会话式的,以窗口/tab进行分配空间,那在多个tab间肯定是不能共享的。

但是当那在当前页面打开控制台,然后进行以下操作:

  1. 在当前页面通过sessionStorage.setItem随意设置一个值,然后通过getItem获取,当然此时肯定会获取到设置的值。
  2. 然后继续在当前页面控制台输入Window.open('http://www.baidu.com')(任意地址都可以,本文以百度为例)
  3. 在跳转的新页面通过getItem获取,此时你会发现能获取到。呀,这就和之前理解的不一样的,从这里看sessionStorage好像也能共享?
  4. 然后在新的页面再次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是复制。

参考文章

【前端缓存】localStorage是同步还是异步的?为什么?

相关推荐
活宝小娜2 小时前
vue不刷新浏览器更新页面的方法
前端·javascript·vue.js
程序视点2 小时前
【Vue3新工具】Pinia.js:提升开发效率,更轻量、更高效的状态管理方案!
前端·javascript·vue.js·typescript·vue·ecmascript
coldriversnow2 小时前
在Vue中,vue document.onkeydown 无效
前端·javascript·vue.js
我开心就好o2 小时前
uniapp点左上角返回键, 重复来回跳转的问题 解决方案
前端·javascript·uni-app
开心工作室_kaic3 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
刚刚好ā3 小时前
js作用域超全介绍--全局作用域、局部作用、块级作用域
前端·javascript·vue.js·vue
沉默璇年4 小时前
react中useMemo的使用场景
前端·react.js·前端框架
yqcoder4 小时前
reactflow 中 useNodesState 模块作用
开发语言·前端·javascript
2401_882727574 小时前
BY组态-低代码web可视化组件
前端·后端·物联网·低代码·数学建模·前端框架
SoaringHeart4 小时前
Flutter进阶:基于 MLKit 的 OCR 文字识别
前端·flutter