这些都很常见,那么问题来了。它们很多都是同源、同站的。假设按小项目划分场景,我在 a 项目中设置了一个 sessionStorage 会话级缓存,那么当前从 a 项目跳转至 b 项目时,b 是可以获取到 a 项目的所有 sessionStorage 的,反之也成立。如果 a 和 b 项目中某个 sessionStorage 的 key 不小心设置成相同的话,那很可能就会影响到对方。localStorage 同理。至于 Cookie 的话,由于它的空间限制最大只允许 4K,因此不适宜存过多数据,一般会存一些像鉴权信息等比较多。同个公司,业务的用户鉴权等是相似的,所以 Cookie 的访问机制也不会有太大的影响。
它们都是浏览器数据存储的方案,是用于解决数据持久化的问题。除此之外,数据也可以存储在内存中(比如挂载到
window
等全局对象下),但这种方式每当页面刷新就会丢失。下面分别从几个方面,详细地介绍
Cookie
、sessionStorage
、localStorage
的区别。空间限制
Cookie
- 大小约为4K
。sessionStorage
和localStorage
- 大小约为5M
。请注意,其中 Cookie 的空间大小指的是
name
、value
以及=
号。另外,这三个不同浏览器下可能会有细微的差异,可忽略。
数量限制
sessionStorage
和localStorage
无数量限制一说。Cookie
是有数量限制的。下表为网上收集(非当前实测结论),看一眼知道有限制这回事就好。
鉴于各浏览器对
Cookie
的空间、数量限制不完全相同,为了较好地兼容,建议如下:没错,面试官问到这么说吧,应该就 OK 了。
另外,与超出空间限制不同的是,超出数量限制之后,是可以继续添加
Cookie
的,但不同浏览器有不同的策略:一些是替换掉最先(老)的Cookie
,有些则是随机替换。作简单了解就好,一般项目不会设那么多的,而且Cookie
过期浏览器是会自动清除的。有效期
Cookie
- 有效期是由 Max-Age 或 Expires 决定的。当Cookie
过期或失效,由浏览器自动删除。Cookie
时不写入这两个属性,那么它的就是会话级别的,即退出浏览器会被销毁。Max-Age
优先级更高。SessionStorage
- 在浏览器标签(或窗口)关闭之前均有效。刷新页面不影响。localStorage
- 若不主动清除,永久有效(“主动”是指由浏览器或脚本清除)。作用域
同源与同站
同源和同站的区别:
eTLD + 1
完全一致。顶级域名和二级域名:
.com
、.cn
、.org
、.net
等等,需要注意的是像.com.cn
、.com.hk
也属于顶级域名。eTLD
(Top-Level Domains)表示有效顶级域名,那么eTLD + 1
就表示二级域名。例如:其中
eTLD
是.com.cn
,那么eTLD + 1
就是.example.com.cn
。完整的 URL(网址)构成如下:
简单来说,只要二级域名相同,就属于同站。同源则要求更严格。因此同源一定同站,反之则不一定。
以下例子,同站,但不同源。
三者的作用域
localStorage
- 这个最简单,必须同源才能访问,在不同标签(或窗口)之间可共享。sessionStorage
- 必须同源,且不同标签(或窗口)之间是不能共享的(这点上盲猜挺多人会理解错的,我当初也理解错了)。Cookie
- 同站是前提,它还受限于具体的Domain
和Path
。理解这些很重要,为什么这么说呢?
作用域引发的问题
很多公司,不是每个 Web 项目对应一个三级域名。
这些都很常见,那么问题来了。它们很多都是同源、同站的。假设按小项目划分场景,我在
a
项目中设置了一个sessionStorage
会话级缓存,那么当前从a
项目跳转至b
项目时,b
是可以获取到a
项目的所有sessionStorage
的,反之也成立。如果a
和b
项目中某个sessionStorage
的key
不小心设置成相同的话,那很可能就会影响到对方。localStorage
同理。至于Cookie
的话,由于它的空间限制最大只允许 4K,因此不适宜存过多数据,一般会存一些像鉴权信息等比较多。同个公司,业务的用户鉴权等是相似的,所以Cookie
的访问机制也不会有太大的影响。针对这些问题,建议是非必要的话,将数据存在内存中,比如用 Vuex、Redux、MobX 等状态管理工具来维护应用的状态。一是信息更不容易暴露,而是可以减少 IO 的读写。但是,这样的话,就要解决数据持久化的问题,因为在内存中的话,只要刷新页面就会丢失。怎么解决?
以 Redux 为例,在创建 Store 时,是可以传入一个初始状态的,它的值取下面这个会话缓存即可。只要监听到状态发生变化变化,并设置或更新
sessionStorage
级别的缓存,将状态缓存起来即可。比如:sessionStorage 鲜为人知的点(冷门)
假设有两个同源页面: A 页面、B 页面对应 URL 为
page_a_url
、page_b_url
。示例一:
示例二:将上述第二步改成下面这样:
结果又是什么呢?直接看下结果:
结论:
同一标签(或窗口)下,所有同源页面将共享
sessionStorage
,同样地,在某个页面修改,将影响其他页面。通过
<a target="_blank" href="page_b_url"></a>
或window.open('page_b_url', 'windowName')
方式打开其他同源页面,有以下特点:sessionStorage
是独立的,互不影响。sessionStorage
缓存为空。后者基于原 Tab 的sessionStorage
拷贝一份,作为新窗口的初始sessionStorage
缓存。需要另外一种情况,当在某页面内嵌套了一个同源的
iframe
,它们之间sessionStorage
是共享的。若非同源页面则不共享。(这一点不完全严谨,具体原因请往下看)总结
Cookie
作用域前提是同站,同时还受限于Domain
和Path
。若两者一致,即可理解为同站共享。sessionStorage
和localStorage
前提必须是同源,其次前者在不同标签(或窗口)相互独立;后者在所有标签之间共享。sessionStorage
初始值会有所差异。通过window.open()
方式,会将原来先的sessionStorage
值拷贝过来,作为其初始值;其他方式初始缓存为空。但注意,后续的sessionStorage
读写操作都是独立对。与服务器通信的差异
sessionStorage
和localStorage
不会主动参与与服务器的通信。Cookie 是保存在浏览器上的一小型文本文件。在每次与服务器的通信中都会携带在 HTTP 请求头之中。
其他
storage 事件的迷惑行为
首先,注册
storage
事件监听器,它只能监听其他同源页面的缓存发生改变时,它才会被触发。得出几个结论:
sessionStorage
是独立的,因此无法监听sessionStorage
的变化,即只能监听localStorage
的变化。当 localStorage 超过 5M 的空间限制之后,若再次 setItem 会怎样?
答案显而易见,这次
setItem()
将会失败,且会抛出错误。针对这种情况,可以做一些处理,比如清空再重新记录等...在 Safari 无痕模式下,对 sessionStorage 操作可能会抛出异常
请看:html5 localStorage error with Safari: "QUOTA_EXCEEDED_ERR: DOM Exception 22: An attempt was made to add something to storage that exceeded the quota."
sessionStorage 在 iframe 的问题
前面提到顶级窗口和 iframe 窗口的页面都是同源的情况下,sessionStorage 是可共享的。但不完全是,比如受
iframe
的 sandbox 属性影响,分为两种情况:假设两个页面同源;情况一
sessionStorage
不共享,在iframe
中是一个全新的sessionStorage
对象。情况二则共享sessionStorage
。References