zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Cookie 和 Storage API 区别与详解

CookieAPI 详解 区别 Storage
2023-09-27 14:25:57 时间
前言

它们都是浏览器数据存储的方案 是用于解决数据持久化的问题。除此之外 数据也可以存储在内存中 比如挂载到 window 等全局对象下 但这种方式每当页面刷新就会丢失。

下面分别从几个方面 详细地介绍 Cookie、sessionStorage、localStorage 的区别。


正文1. 空间限制


Cookie - 大小约为 4K。sessionStorage 和 localStorage - 大小约为 5M。

请注意 其中 Cookie 的空间大小指的是 name、value 以及 号。

另外 这三个不同浏览器下可能会有细微的差异 可忽略。

在所有浏览器中 若 Cookie 大小已超出空间限制 后续设置的新 Cookie 就会被忽略。


2. 数量限制


sessionStorage 和 localStorage 无数量限制一说。Cookie 是有数量限制的。

下表为网上收集 非当前实测结论 看一眼知道有限制这回事就好。

浏览器大小数量IE64095 个字节每个域 20 个IE7/84095 个字节每个域 50 个Opera4096 个字节每个域 30 个FireFox4097 个字节每个域 50 个Safari4097 个字节没有数量限制Chrome4097 个字节每个域 53 个

若有兴趣 实测可以看这个网站 Browser Cookie Limits。作者简单跑了下 Chrome 每个域是 180 个 Firefox 很快卡住了 风扇嗡嗡响...... 测试结果 Firefox 和 Safari 都 N/A 没数据 应该是没数量限制吧。

鉴于各浏览器对 Cookie 的空间、数量限制不完全相同 为了较好地兼容 建议如下

总共 300 个 Cookie每个 Cookie 大小为 4096 个字节每个域 20 个 Cookie每个域 81920 个字节 20 × 4096 。

没错 面试官问到这么说吧 应该就 OK 了。

另外 与超出空间限制不同的是 超出数量限制之后 是可以继续添加 Cookie 的 但不同浏览器有不同的策略 一些是替换掉最先 老 的 Cookie 有些则是随机替换。作简单了解就好 一般项目不会设那么多的 而且 Cookie 过期浏览器是会自动清除的。


3. 有效期


Cookie - 有效期是由 Max-Age 或 Expires 决定的。当 Cookie 过期或失效 由浏览器自动删除。若在设置 Cookie 时不写入这两个属性 那么它的就是会话级别的 即退出浏览器会被销毁。若同时存在时 Max-Age 优先级更高。SessionStorage - 在浏览器标签 或窗口 关闭之前均有效。刷新页面不影响。localStorage - 若不主动清除 永久有效 “主动”是指由浏览器或脚本清除 。

请注意

讲道理的话 会话级的 Cookie 在浏览器退出时就会被删除。但是有些浏览器不讲武德 比如 Chrome。在某些情况下 关闭浏览器重新进入 会话级别的 Cookie 并不会删除 在 Application 选项的 Cookie 栏还能看到。

原因可能是 Chrome - 设置 - 启动时 选择了 打开新标签 之外的选项。切换过来就好了...


4. 作用域


这块内容比较多...


铺垫 - 了解同源和同站的区别

区别如下


同源 Same-Origin 协议 protocol 主机名 hostname 端口 port 完全一致。同站 Same-Site eTLD 1 完全一致。


铺垫 - 顶级域名和二级域名的认知


关于顶级域名、二级域名 也有很多认知问题。

顶级域名 也称为“一级域名” 常见的有 .com、.cn、.org、.net 等等 需要注意的是像 .com.cn、.com.hk 也属于顶级域名。二级域名 就是顶级域名的下一级域名。


在国内 很多资料认为 顶级域和一级域是分开的。比如 .baidu.com 如果按照这种方式划分 那么.com 是顶级域名 .baidu.com 就是一级域名。好像阿里云就是这样定义的。


而我更偏向于认为 .baidu.com 属于二级域名。不用过分纠结 在团队内统一即可。

eTLD Top-Level Domains 表示有效顶级域名 那么 eTLD 1 就表示二级域名。例如

https://www.example.com.cn


其中 eTLD 是 .com.cn 那么 eTLD 1 就是 .example.com.cn。关于 eTLD 更多请看这里


铺垫 - 区分同源和同站


先看下 完整的 URL 网址 构成如下


22.webp.jpg

简单来说 只要二级域名相同 就属于同站。同源则要求更严格。因此同源一定同站 反之则不一定。

以下例子 同站 但不同源。

http://a.example.com:80

http://b.example.com:80


正题 - 三者的作用域


localStorage - 这个最简单 必须同源才能访问 在不同标签 或窗口 之间可共享。sessionStorage - 必须同源 且不同标签 或窗口 之间是不能共享的 这点上盲猜挺多人会理解错的 我当初也理解错了 。Cookie - 同站是前提 它还受限于具体的 Domain 和 Path。

理解这些很重要 为什么这么说呢


拓展 - 作用域引发的问题


很多公司 不是每个 Web 项目对应一个三级域名。

// 主营业务、较为重要的业务 可能会这样去区分 

https://project-a.company.com

https://project-b.company.com

// 但一些非业务性、或者一些小项目 很可能是这样的 

https://sub.company.com/project-a/index.html

https://sub.company.com/project-b/index.html


这些都很常见 那么问题来了。它们很多都是同源、同站的。假设按小项目划分场景 我在 a 项目中设置了一个 sessionStorage 会话级缓存 那么当前从 a 项目跳转至 b 项目时 b 是可以获取到 a 项目的所有 sessionStorage 的 反之也成立。如果 a 和 b 项目中某个 sessionStorage 的 key 不小心设置成相同的话 那很可能就会影响到对方。localStorage 同理。至于 Cookie 的话 由于它的空间限制最大只允许 4K 因此不适宜存过多数据 一般会存一些像鉴权信息等比较多。同个公司 业务的用户鉴权等是相似的 所以 Cookie 的访问机制也不会有太大的影响。

针对这些问题 建议是非必要的话 将数据存在内存中 比如用 Vuex、Redux、MobX 等状态管理工具来维护应用的状态。一是信息更不容易暴露 而是可以减少 IO 的读写。但是 这样的话 就要解决数据持久化的问题 因为在内存中的话 只要刷新页面就会丢失。怎么解决

以 Redux 为例 在创建 Store 时 是可以传入一个初始状态的 它的值取下面这个会话缓存即可。只要监听到状态发生变化变化 并设置或更新 sessionStorage 级别的缓存 将状态缓存起来即可。比如

import { createStore } from redux 

// 定义一个可按项目划分的 key

const { host, pathname } window.location

const stateCacheKey cache_state_${host}_${pathname} 

// 设置 store 的初始值 取 stateCacheKey 缓存的值 首次为空对象

const initialState JSON.parse(sessionStorage.getItem(stateCacheKey)) || {}

// 创建 Store reducers、middlewares 非本文讨论重点省略... 

const reducers (state {}, action) { /* some reducers... */ }

const store createStore(reducers, initialState) // 这里省略了中间件

// 监听状态 每次状态变化 stateCacheKey 都得以更新

const unsubscribe store.subscribe(() {

 const currentStateStr JSON.stringify(store.getState())

 window.sessionStorage.setItem(stateCacheKey, currentStateStr)

// 解除监听 这样 store.unsubscribe() 调用即可

store.unsubscribe unsubscribe

// 个人习惯 也将 store 挂载全局 以备特殊情况调用

window.store store

// 作为模块导出 并传入 react-redux 的 Provider 组件

export default store


注意 - sessionStorage 鲜为人知的点 冷门


下面例子 均在同源情况下。假设有两个同源页面 A 页面、B 页面对应 URL 为 page_a_url、page_b_url。

示例一

// 1. 在 A 页面 设置一个会话缓存 keyA

sessionStorage.setItem( keyA , 123 )

// 2. 若 A 有一个链接 可跳转至 B 页面 将会以新窗口的形式打开 B 页面 

 a target _blank href page_b_url To Page B /a 

// 3. 点击链接 跳转到 B 页面

sessionStorage.getItem( keyA ) // ❓ 打印结果是什么 

// 4. 接着 在 B 页面中 设置另一个会话缓存 keyB

sessionStorage.setItem( keyB , 456 )

// 5. 切换至 A 页面 打印一下 

sessionStorage.getItem( keyB ) // ❓ 打印结果又是什么 

// 6. 在 A 页面中再次设置一个缓存 keyB

sessionStorage.setItem( keyB , 789 )

// 7. 再次切换至 B 页面的窗口 

sessionStorage.getItem( keyB ) // ❓ 打印结果是 456 还是 789 呢 


示例二 将上述第二步改成下面这样

 a onclick window.open( page_b_url , DescriptiveWindowName ) To Page B /a 


结果又是什么呢 直接看下结果

示例一 依次打印出 null、null、 456 

示例二 依次打印出 123 、null、 456 


结论


同一标签 或窗口 下 所有同源页面将共享 sessionStorage 同样地 在某个页面修改 将影响其他页面。
通过 a target _blank href page_b_url /a 或 window.open( page_b_url , windowName ) 方式打开其他同源页面 有以下特点 两个 Tab 之间的 sessionStorage 是独立的 互不影响。区别点在于 打开新窗口时 初始缓存不一样 前者的初始 sessionStorage 缓存为空。后者基于原 Tab 的 sessionStorage 拷贝一份 作为新窗口的初始 sessionStorage 缓存。前者表现与手动创建新窗口是一致的。需要另外一种情况 当在某页面内嵌套了一个同源的 iframe 它们之间 sessionStorage 是共享的。若非同源页面则不共享。 这一点不完全严谨 具体原因请往下看


总结 - 作用域


Cookie 作用域前提是同站 同时还受限于 Domain 和 Path。若两者一致 即可理解为同站共享。sessionStorage 和 localStorage 前提必须是同源 其次前者在不同标签 或窗口 相互独立 后者在所有标签之间共享。除此之外 以不同方式创建新标签 或窗口 它的 sessionStorage 初始值会有所差异。通过 window.open() 方式 会将原来先的 sessionStorage 值拷贝过来 作为其初始值 其他方式初始缓存为空。但注意 后续的 sessionStorage 读写操作都是独立对。


5. 与服务器通信的差异


sessionStorage 和 localStorage 不会主动参与与服务器的通信。

Cookie 是保存在浏览器上的一小型文本文件。在每次与服务器的通信中都会携带在 HTTP 请求头之中。

但是会有一些限制 另起一文 请稍等...


6. 拓展 进阶


Q1 storage 事件的迷惑行为

首先 注册 storage 事件监听器 它只能监听其他同源页面的缓存发生改变时 它才会被触发。

得出几个结论

用于监听其他页面的缓存变化 而同一页面内的缓存变化 都不起作用。 奇葩吧 由于不同 Tab 之间 sessionStorage 是独立的 因此无法监听 sessionStorage 的变化 即只能监听 localStorage 的变化。“发生改变”包括 创建、更新、删除。但重复设置相同的 key-value 不会触发该事件 它至多在首次创建时触发。

const listener function (e) {

 // key: 对应缓存 key

 // newValue: 该 key 新设置的缓存值

 // oldValue: 该 key 对应的旧缓存值

 // storageArea: 对应为 window.localStorage 对象

 // storageArea: 触发该事件所对应的 URL

window.addEventListener( storage , listener)


Q2 当 localStorage 超过 5M 的空间限制之后 若再次 setItem 会怎样

答案显而易见 这次 setItem() 将会失败 且会抛出错误。针对这种情况 可以做一些处理 比如清空再重新记录等...

for (let i i i ) {

 try {

 localStorage.setItem( key , value )

 break

 } catch (e) {

 // 清空 并重试

 // QuotaExceededError: The quota has been exceeded. localStorage缓存超出限制

 localStorage.clear()

}


Q3 在 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.

Q4 sessionStorage 在 iframe 的问题

建议少用 iframe 尽管目前很多大网站仍然使用它。

前面提到顶级窗口和 iframe 窗口的页面都是同源的情况下 sessionStorage 是可共享的。但不完全是 比如受 iframe 的 sandbox 属性影响 分为两种情况

// 1️⃣

 iframe sandbox 

// 2️⃣

 iframe sandbox allow-same-origin 


假设两个页面同源 情况一 sessionStorage 不共享 在 iframe 中是一个全新的 sessionStorage 对象。情况二则共享 sessionStorage。


7. References


Web Storage APIWeb Storage 的使用和进阶知识
Servlet API Cookie和Session 我们在前文【和面试官畅谈「HTTP协议」 3.3.6】中已经提及了Servlet在会话管理操作中的Cookie与Session,但在上篇文章中有些过于理论化,在这篇文章中,我们结合具体的API和综合案例来更深刻的理解Cookie和Session。
Servlet第六篇【Session介绍、API、生命周期、应用、与Cookie区别】(七) Session 是另一种记录浏览器状态的机制。不同的是Cookie保存在浏览器中,Session保存在服务器中。用户使用浏览器访问服务器的时候,服务器把用户的信息以某种的形式记录在服务器,这就是Session
Servlet第六篇【Session介绍、API、生命周期、应用、与Cookie区别】(六) Session 是另一种记录浏览器状态的机制。不同的是Cookie保存在浏览器中,Session保存在服务器中。用户使用浏览器访问服务器的时候,服务器把用户的信息以某种的形式记录在服务器,这就是Session
Servlet第六篇【Session介绍、API、生命周期、应用、与Cookie区别】(五) Session 是另一种记录浏览器状态的机制。不同的是Cookie保存在浏览器中,Session保存在服务器中。用户使用浏览器访问服务器的时候,服务器把用户的信息以某种的形式记录在服务器,这就是Session
Servlet第六篇【Session介绍、API、生命周期、应用、与Cookie区别】(四) Session 是另一种记录浏览器状态的机制。不同的是Cookie保存在浏览器中,Session保存在服务器中。用户使用浏览器访问服务器的时候,服务器把用户的信息以某种的形式记录在服务器,这就是Session
Servlet第六篇【Session介绍、API、生命周期、应用、与Cookie区别】(三) Session 是另一种记录浏览器状态的机制。不同的是Cookie保存在浏览器中,Session保存在服务器中。用户使用浏览器访问服务器的时候,服务器把用户的信息以某种的形式记录在服务器,这就是Session
Servlet第六篇【Session介绍、API、生命周期、应用、与Cookie区别】(二) Session 是另一种记录浏览器状态的机制。不同的是Cookie保存在浏览器中,Session保存在服务器中。用户使用浏览器访问服务器的时候,服务器把用户的信息以某种的形式记录在服务器,这就是Session
Servlet第六篇【Session介绍、API、生命周期、应用、与Cookie区别】(一) Session 是另一种记录浏览器状态的机制。不同的是Cookie保存在浏览器中,Session保存在服务器中。用户使用浏览器访问服务器的时候,服务器把用户的信息以某种的形式记录在服务器,这就是Session
Servlet第五篇【介绍会话技术、Cookie的API、详解、应用】(六) 基本概念: 指用户开一个浏览器,访问一个网站,只要不关闭该浏览器,不管该用户点击多少个超链接,访问多少资源,直到用户关闭浏览器,整个这个过程我们称为一次会话.