浏览器存储

本文最后更新于 2025年8月11日 晚上

参考:
https://juejin.cn/post/6844903516826255373
https://juejin.cn/post/6844903989096497159
https://juejin.cn/post/6844904021308735502#heading-13

前端本地存储方式有三种,分别是 Cookie、WebStorage、IndexedDB。

Cookie

Cookie 最开始被设计出来并不是来做本地存储的,而是为了弥补 HTTP 在状态管理上的不足。HTTP 协议是一个无状态协议,客户端向服务器发请求,服务器返回响应,故事就结束了,但是下次发请求如何让服务端知道客户端是谁呢?在这种背景下就产生了 Cookie。

Cookie 是 HTTP 协议的一部分,也是服务器发送到用户浏览器并保存在本地的一小块数据,内部以键值对的方式来存储。向同一个域名下发送请求会携带相同的 Cookie,服务器拿到 Cookie 进行解析以便能拿到客户端的状态。在 HTTP 中有:Cookie,在浏览器中有:cookie

作用

Cookie 主要用于以下三个方面:

  • 会话状态管理
    如用户登录状态、购物车、游戏分数或其他需要记录的信息。
  • 个性化设置
    如用户自定义设置、主题和其他设置。
  • 浏览器行为跟踪
    如跟踪分析用户行为等。

Cookie 曾一度用于客户端数据的存储,因为当时并没有其他合适的存储办法而作为唯一的存储手段,但现在有 WebStorage 和 IndexedDB。同时,Cookie 也有着以下缺陷:

  • 容量缺陷
    Cookie 的体积上限只有 4KB,只能用来存储少量的信息。
  • 性能缺陷
    Cookie 紧跟域名,不管域名下面的某个地址需不需要 Cookie,请求时都会携带完整的 Cookie,这样随着请求数的增多,其实会造成巨大的性能浪费,因为请求会携带很多不必要的内容。
  • 安全缺陷
    由于 Cookie 以纯文本的形式在浏览器和服务器当中传递,所以很容易被第三方截获或者篡改。在 Cookie 的有效期内重新发送给服务器是相当危险的。

属性

协议匹配 Secure

Secure 属性控制禁止通过 HTTP 协议传输 Cookie(防止中间人窃取),仅允许 HTTPS 协议。

1
Set-Cookie: Session=abc; Secure;  // 仅 HTTPS 连接发送

域名匹配 Domain

Domain 属性用于限制能访问修改 Cookie 的域名,不用于限制端口以及协议。默认情况下,不同的域名不能直接访问彼此 Cookie,但通过合理设置 Domain 属性可以实现单向或双向共享。

对于 Domain 属性,可以设置当前域或者祖父域但不能到顶级域),不能设置子域或者其他域设置了也无效),但也可以不设置此属性。不设置此属性时,只有当前域可以访问修改该 Cookie;显式设置此属性为某个域后,设置的域及其所有子域可以访问修改该 Cookie。如:

对于 a.com 站点的 Cookie:

  • 可以不设置 Domain
    效果:只有 a.com 可以访问修改
  • 可以设置 Domain=a.com(等同 Domain=.a.com
    效果:a.comsub.a.comapi.a.comsub.api.a.com 都可以访问修改
  • 不可以设置 Domain=api.a.comDomain=b.comDomain=com(等同 Domain=.com

对于 api.a.com 站点的 Cookie:

  • 可以不设置 Domain
    效果:只有 api.a.com 可以访问修改
  • 可以设置 Domain=api.a.com
    效果:api.a.comsub.api.a.com 可以访问修改,a.comsub.a.com 不可以访问修改
  • 可以设置 Domain=a.com(等同 Domain=.a.com
    效果:a.comsub.a.comapi.a.comsub.api.a.com 都可以访问修改
  • 不可以设置 Domain=sub.api.a.comDomain=sub.a.comDomain=api.b.com

对于 sub.api.a.com站点的 Cookie:

  • 可以不设置 Domain
    效果:只有 sub.api.a.com 可以访问修改
  • 可以设置 Domain=sub.api.a.comDomain=api.a.comDomain=a.com
    具体效果略
  • 不可以设置略
    具体效果略
1
2
3
4
// 当前域名 a.com 返回的 HTTP 响应头
Set-Cookie: Session_id=abc123; Domain=.a.com; Path=/; SameSite=Lax; Secure
// 当前域名 a.com 或任意子域的前端执行,共享到所有子域
document.cookie = "user_token=xyz; domain=.a.com; path=/; secure";

路径匹配 Path

Path 属性用于限制能访问修改 Cookie 的路径,默认为当前路径。对于 a.com 站点,如果设置了 Set-Cookie: temp=123; Domain=a.com; Path=/admin; 那么:

  • a.com/admin 可以用此 Cookie
  • a.com/user 不可以用此 Cookie
  • api.a.com/admin 可以用此 Cookie
  • api.a.com/user 不可以用此 Cookie

站点匹配 SameSite

SameSite 属性控制在跨站请求(Cross-site request)时是否携带 Cookie,用于防止 CSRF 攻击。该属性共有三种值,默认值为 Lax:

意义 适用场景
Strict 完全禁止跨站携带,只有同站请求才带 Cookie,跨站的所有请求(包括 GET)都不带 强安全,如银行、后台管理系统
Lax 同站请求会带;跨站请求只有安全的 GET 顶级导航带(表单 POST 不会带) 登录后的页面跳转(防止 CSRF,同时允许常规跳转)
None 所有请求都会带 Cookie(包括跨站 POST、iframe、图片加载等),但必须同时设置 Secure(仅限 HTTPS) CDN、SSO、广告跟踪等

需要注意:

  • 主域相同的站点就是同站(same-site)(a.comsub.a.comapi.a.com 都算同站)
  • 跨站是指主域不同(a.comb.com
  • 如果 SameSite=None,浏览器要求必须加 Secure,否则直接忽略 Cookie

分区存储 Partitioned

Partitioned 属性解决第三方 Cookie 在隐私安全下的存储问题,特别是在浏览器逐渐限制第三方 Cookie 的背景下。

传统第三方 Cookie(例如在 siteA.com 里嵌入 iframe 加载 siteB.com 内容)可以跨网站共享,这对广告追踪等行为非常方便,但也带来了隐私泄露和跨站跟踪风险。为了保护用户隐私,Chrome、Firefox、Safari 等浏览器正在逐步阻止第三方 Cookie,但有些合法场景也需要第三方 Cookie,比如跨站嵌入的支付网关、CDN 登录状态共享、多站点业务集群下的用户会话保持等。

开启 Partitioned 属性后,让第三方 Cookie 隔离到特定的第一方站点上下文中,避免跨站追踪,但又能在嵌入的场景下工作。假设有个站点被两个不同的站点使用:shopA.com 嵌入了 cdn.comshopB.com 也嵌入了 cdn.com

  • 没有 Partitioned
    • cdn.com 的第三方 Cookie 会在 shopA.comshopB.com 间共享,导致跨站追踪风险
  • 有 Partitioned
    • shopA.com 里访问 cdn.com 时,Cookie 被分配到 (cdn.com, shopA.com) 分区
    • shopB.com 里访问 cdn.com 时,Cookie 被分配到 (cdn.com, shopB.com) 分区

这样 cdn.com 在两个不同站点上下文里的 Cookie 是完全独立的,无法互相读取或跟踪。而且:

  • 存储:浏览器会把 Cookie 存在一个“第一方上下文分区”下,而不是 cdn.com 的全局 Cookie 下
  • 读取:只有在相同的第一方上下文中,才能读取这个 Cookie
  • 安全性:就算同一个第三方域名被不同网站引用,也不会共享 Cookie 数据,防止广告跨站追踪

需要注意的是,使用 Partitioned 属性的必要条件有四个:

  • 设置SameSite=None(第三方 Cookie 必须允许跨站发送)
  • 设置Secure(必须通过 HTTPS 才能传输才能保证安全性)
  • 禁止设置Domain(分区 Cookie 指定作用域会导致设置失败)
  • 仅在嵌套上下文中有效(如 iframe 等),在主窗口设置无效
1
Set-Cookie: session_id=abc123; Secure; SameSite=None; Partitioned

存储管理 Priority

Priority 属性用于管理浏览器存储空间不足时 Cookie 的清理优先级。虽然非官方标准,但在存储优化中扮演关键角色。该属性共有三种值,默认值为 Medium:

优先级 适用场景
Low 最先清理 分析跟踪、广告标识符
Medium 其次清理 用户偏好设置、功能标记
High 最后清理 会话令牌、身份验证信息
1
Set-Cookie: session_id=abc123; Priority=high; Path=/; Secure; HttpOnly

需要注意:

  • 这个属性不会让 Cookie 永不过期,只是在“被迫删掉时”尽量保留
  • 如果 Cookie 已经过期(ExpiresMax-Age 到期),优先级不会保它
  • 它和 SameSiteSecureHttpOnly 等属性互不影响
  • 在不支持的浏览器上,它会被当作普通未知属性直接忽略

防窃取 HttpOnly

HttpOnly 属性禁止 JS 代码访问 Cookie(document.cookie 无法读取),用于防止 XSS 攻击。

1
Set-Cookie: token=xyz; HttpOnly;   // 仅服务端可读

有效期 Expires&Max-Age

通过设置 Expires 属性(指定过期日期和时间)或 Max-Age 属性(指定秒数后过期),可以指定 Cookie 过期时间使其在一段时间后失效。Chrome 从 104 版本开始,限制 Cookie 的最大存储期限为 400 天,未设置 ExpiresMax-Age 的 Cookie 称为会话 Cookie,在浏览器关闭时会被清除。

1
2
Max-Age=3600
Expires=Wed, 21 Oct 2025 07:28:00 GMT

使用

可通过 document.cookie 获取全部 Cookie。Cookie 是一段字符串,是键值对的形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 存储 Cookie 值
const dataCookie = "110";
document.cookie = "token" + "=" + dataCookie;

// 获取指定名称的 Cookie 值
function getCookie(name) {
const arr = document.cookie.match(
new RegExp("(^| )" + name + "=([^;]*)(;|$)")
);
if (arr != null) {
console.log(arr);
return unescape(arr[2]);
}
return null;
}

// 存储 Cookie 值并且设置 Cookie 过期时间
function setTime() {
const date = new Date();
const expiresDays = 10; // 设置十天过期
date.setTime(date.getTime() + expiresDays * 24 * 3600 * 1000);
document.cookie = "userId=828; expires=" + date.toGMTString();
console.log(document.cookie, "存储Cookie值并且设置Cookie过期时间");
}

// 删除 Cookie
function delCookie(CookieName1) {
var date2 = new Date();
date2.setTime(date2.getTime() - 10001); // 把时间设置为过去的时间,会自动删除
document.cookie = CookieName1 + "=v; expires=" + date2.toGMTString();
console.log(document.cookie, "删除Cookie");
}

原生操作起来有些麻烦,可引入封装好的库进行使用,比如 js-Cookie:

1
2
3
Cookies.set("name", "value", { expires: 7 }); // 设置一个 Cookie,7天后失效
Cookies.get("name");
Cookies.remove("name");

区别

Cookie 和 Session 都是普遍用来跟踪浏览用户身份的会话方式。

  • Cookie 数据存放在客户端,Session 数据放在服务器端,而且后者是依赖于 Cookie 实现的。
  • Cookie 本身并不安全,考虑到安全应当使用 Session。
  • Session 会在一定时间内保存在服务器上。如果访问量比较大,会比较消耗服务器的性能。如果考虑到减轻服务器性能方面的开销,应当使用 Cookie。
  • 单个 Cookie 保存的数据大小不能超过 4K,很多浏览器都限制一个域名最多保存 50 个 Cookie。可以将登陆等重要信息存放到 Session,其他信息如果需要保留,可以存放到 Cookie。

WebStorage

使用 Cookie 时受到种种限制,最关键是容量太小和无法持久化存储。在 HTML5 的标准下,出现了 WebStorage(又分为 localStorage 和 sessionStorage)。WebStorage 是浏览器基于 HTML5 标准自身的实现,跟 HTTP 无关。

作用域

WebStorage 的作用域规则与 Cookie 不同,它严格遵循浏览器的同源策略(Same-Origin Policy),不同的源之间无法互相访问对方的存储数据。

WebStorage 下的 localStorage 和 sessionStorage 的区别在于,在浏览器的同源但不同的标签页,可以共享 localStorage 数据但无法共享 sessionStorage 数据。localStorage 是一个域名的本地存储(永久存储),sessionStorage 是一个域名的会话存储(关闭页面后消失)。

使用

localStoragesessionStorage 都是 WebStorage 类型实例,使用方法一样。注意事项:

  • 写入时如果超出容量会报错,之前保存的数据不会丢失
  • 存储容量快满时 getItem() 方法性能会急剧下降
  • 存储的都是字符串(想要存储对象需调用 JSON.stringify() 方法转为 JSON 数据类型;想要获取数据需调用 JSON.parse() 方法再解析 JSON 数据类型成对象)
1
2
3
4
5
6
7
8
9
10
11
12
13
let name = "SessionData",
num = 120;
sessionStorage.setItem(name, num); // 存储数据
sessionStorage.value = 110; // 存储数据
sessionStorage.valueOf(); // 获取全部数据
//  {value: '110', SessionData: '120', length: 2}
let dataSession1 = sessionStorage.getItem(name); // 获取指定键名数据
let dataSession2 = sessionStorage.SessionData; // 获取指定键名数据
let dataSession = sessionStorage; // 获取全部数据
console.log(dataSession1, dataSession2, dataSession);
// 120 120 {value: '110', SessionData: '120', length: 2}
sessionStorage.removeItem(name); // 删除指定键名数据
sessionStorage.clear(); // 清空缓存数据

对比

在浏览器开发者面板的 Application 栏可以看到对应的具体信息。

不同:

种类 大小 有效期 HTTP 请求 使用场景
Cookie 4KB 可设置失效时间,默认关浏览器失效 每次网络请求都携带 识别用户登录
localStorage 5MB 除非被手动清除,否则永久保存 不与服务端通信 持久化缓存数据,可以跨页面传递内容、页面默认偏好配置
sessionStorage 5MB 仅在当前网页会话有效,关闭页面后失效 不与服务端通信 保存临时数据,可以用于对表单信息维护,存储本次浏览记录

相同:

  • 存储数据类型都是字符串。
  • 都受到跨域限制。

IndexedDB

IndexedDB 是运行在浏览器中的非关系型数据库,本质上是数据库,而且不是和 WebStorage 的 5M 在一个量级,理论上这个容量没有上限。关于它的使用,MDN 教程文档已经非常详尽。

分析一下 IndexedDB 的一些重要特性,除了拥有数据库本身的特性,比如支持事务,存储二进制数据等等,还有这样一些特性需要格外注意:

  • 键值对存储。内部采用对象仓库存放数据,在这个对象仓库中数据采用键值对的方式来存储。
  • 异步操作。数据库的读写属于 I/O 操作, 浏览器中对异步 I/O 提供了支持。
  • 受同源策略限制。即无法访问跨域的数据库。

浏览器存储
https://xuekeven.github.io/2021/09/09/浏览器存储/
作者
Keven
发布于
2021年9月9日
更新于
2025年8月11日
许可协议