浏览器

浏览器本地存储

总结梳理浏览器本地存储的五种方案

Yixuan Lang
2022-02-12
7 min

# 浏览器本地存储

近年来,随着浏览器存储技术的提升,出现了多种前端存储方式。比如:cookielocalStoragesessionStorageWeb SQLindexedDB。 对于前三种方式想必大家耳熟能详,因为 cookie、localStorage、sessionStorage 都属于 js 语言的本地存储,而 Web SQL 和 indexedDB 则是一种前端存储数据库。 这篇文章会分别介绍这五种存储方式,以及他们之间的差异及优缺点。

# 一、Cookie

Cookie是最早被提出来的本地存储⽅式,在此之前,服务端是⽆法判断⽹络中的两个请求是否是同⼀⽤户发起的,也就是无法辨别用户身份,为解决这个问题,Cookie 就出现了。Cookie 的⼤⼩只有 4kb,它是⼀种纯⽂本⽂件,每次发起 HTTP 请求都会携带 Cookie。

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

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

Cookie总是保存在客户端中,按在客户端中的存储位置,Cookie 可以分为内存 Cookie 和硬盘 Cookie

  • 内存Cookie由浏览器进行维护,保存在内存中,浏览器关闭时 Cookie 就会消失,其存在时间是短暂的。
  • 硬盘Cookie保存在硬盘中,有一个过期时限,用户手动清除或者过期时间到了,才会被清除。

如何判断一个 cookie 是内存 cookie 还是硬盘 cookie?

  • 没有设置过期时间,默认情况下 cookie 是内存 cookie,在关闭浏览器时会自动清除。
  • 有设置过期时间,并且过期时间不为 0 或者负数的 cookie,时硬盘 cookie,需要手动或者到期,才会删除。
image-20220409154738991 image-20220409154804599
  • Cookie 一旦创建成功,名称就无法修改
  • Cookie 的大小受限,一般为 4kb,同一个域名下存放的 Cookie个数有限
  • Cookie无法进行跨域,也就是说 a 域名和 b 域名下的 cookie 时无法共享的,这是由 Cookie 的隐私安全性决定的,这样就能够阻止非法获取其他网站的 Cookie
  • 每次发起同域下的 HTTP 请求,都会携带当前域名下的 Cookie
  • 支持设置HttpOnly,防止 Cookie 被客户端的 JavaScript 访问
Cookie原理

第一次访问网站的时候,浏览器发出请求,服务器响应请求后,会在响应头里面添加一个 Set-Cookie 选项,将 Cookie 放入到响应请求中,在浏览器第二次发请求的时候,会通过 Cookie 请求头部将 Cookie 信息发送给服务器,服务端会辨别用户身份,另外 Cookie 的过期时间、域、路径、有效期、适用站点都可以根据需要来指定。

字段名 作用
Name cookie 的名称
Value cookie 的值,对于认证 cookie,value 值包括 web 服务器所提供的访问令牌;
Size cookie 的⼤⼩
Path 访问此 cookie 的⻚⾯路径
Secure 指定是否使⽤ HTTPS 安全协议发送 Cookie
Domain 可以访问该 cookie 的域名,Cookie 机制并未遵循严格的同源策略,允许⼀个⼦域可以设置或获取其⽗域的 Cookie。
HTTPOnly 规定了这个 cookie 只能被服务器访问,不能使⽤ js 脚本访问
Expires/Max-size 设置 cookie 的超时时间。不设置的话默认值是 Session,意思是 cookie 会和 session ⼀起失效。当浏览器关闭(不是浏览器标签⻚,⽽是整个浏览器) 后,此 cookie 失效。
document.cookie = "username=langyixuan;"; // 创建一个名为username的cookie
document.cookie = "expires=Thu, 18 Dec 2043 12:00:00 GMT;"; // 设置cookie的有效日期
var x = document.cookie;

删除 Cookie 非常简单,只需要设置expires参数为以前的时间即可,删除时不必指定 Cookie 的值。

document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 GMT";

# 封装函数

// 添加cookie
function setCookie(key, value, iDay) {
  var oDate = new Date();
  oDate.setDate(oDate.getDate() + iDay);
  document.cookie = key + "=" + value + ";expires=" + oDate;
}
// 删除cookie
function removeCookie(key) {
  setCookie(key, "", -1); //这里只需要把Cookie保质期退回一天便可以删除
}
// 获取cookie
function getCookie(key) {
  var cookieArr = document.cookie.split("; ");
  for (var i = 0; i < cookieArr.length; i++) {
    var arr = cookieArr[i].split("=");
    if (arr[0] === key) {
      return arr[1];
    }
  }
  return false;
}

虽然 Cookie 使用起来很方便,但是它还是存在着一些缺陷,如下:

  • Cookie 不够大,Cookie 的大小限制在 4KB 左右
  • Cookie 是紧跟域名的,同一个域名下的所有请求,都会携带 Cookie,过多的 Cookie 会带来巨大的性能浪费
  • 由于在 HTTP 请求中的 Cookie 是明文传递的,所以安全性成问题,除非用 HTTPS

另外我们还需要注意 Cookie 的安全性

属性 作用
value 如果用于保存用户登录态,应该将该值加密,不能使用明文的用户标识
http-only 不能通过 JavaScript 访问 Cookie,减少 XSS 攻击
secure 只能在协议为 HTTPS 的请求中携带
same-site 规定浏览器不能在跨域请求中携带 Cookie,减少 CSRF 攻击

HttpOnly 不支持读写,浏览器不允许脚本操作去更改 Cookie,所以为避免跨域脚本(XSS)攻击,通过 JavaScriptdocument.cookie 无法访问带有 HttpOnly 标记的 Cookie,它们只应该发送给服务端,如果包含服务端 Session 信息的 Cookie 不想被客户端 JavaScript 脚本调用,那么就应该为其设置 HttpOnly 标记,如下:

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2018 07:28:00 GMT; Secure; HttpOnly

标记为 SecureCookie 只应通过被 HTTPS 协议加密过的请求发送给服务端,但即便设置了 Secure 标记,敏感信息也不应该通过 Cookie传输,因为 Cookie 有其固有的不安全性,Secure 标记也无法提供确实的安全保障。

# 二、LocalStorage 和 SessionStorage

LocalStorage与SessionStorage是 HTML5 新引⼊的特性,用于进行本地存储。由于有的时候我们存储的信息较⼤,Cookie 就不能满⾜我们的需求,这时候LocalStorage和SessionStorage就派上⽤场了。

LocalStorage 与 SessionStorage 两者的使用非常相似,只是在存储时效上有差异。

# 💙 LocalStorage

一种 持久化的存储方式 ,也就是说如果不手动清除,数据就永远不会过期,它是采用键值对的方式存储数据,按域名将数据分别保存到对应数据库文件里,相比 Cookie 来说,它能保存更大的数据。

👉 LocalStorage 的特点有以下:

  • 大小限制为 5MB ~ 10MB
  • 在同源的所有标签页和窗口之间共享数据
  • 数据仅保存在客户端,不与服务器进行通信
  • 数据持久存在且不会过期,重启浏览器后仍然存在
  • 对数据的操作是同步的

👉 LocalStorage 的优点:

  • 在⼤⼩⽅⾯,LocalStorage 的⼤⼩⼀般为 5MB,可以储存更多的信息
  • LocalStorage 是持久储存,并不会随着⻚⾯的关闭⽽消失,除⾮主动清理,不然会永久存在
  • 仅储存在本地,不像 Cookie 那样每次 HTTP 请求都会被携带

👉 LocalStorage 的缺点:

  • 存在浏览器兼容问题,IE8 以下版本的浏览器不⽀持
  • 如果浏览器设置为隐私模式,那我们将⽆法读取到 LocalStorage
  • LocalStorage 受到同源策略的限制,即端⼝、协议、主机地址有任何⼀个不相同,都不会访问

👉 LocalStorage 具体使用:

// 通过 setItem() 增加一个数据项
localStorage.setItem("myName", "zhangsan");

// 通过 getItem() 获取某个数据项
let me = localStorage.getItem("myName");

// 通过 removeItem() 移除某个数据项
localStorage.removeItem("myName");

// 移除所有数据项
localStorage.clear();

👉 LocalStorage 的使用场景:

  • 有些⽹站有换肤的功能,这时候就可以将换肤的信息存储在本地的 LocalStorage 中,当需要换肤的时候,直接操作 LocalStorage 即可
  • 在⽹站中的⽤户浏览信息也会存储在 LocalStorage 中,还有⽹站的⼀些不常变动的个⼈信息等也可以存储在本地的 LocalStorage 中

# 💚 SessionStorage

SessionStorage 和 LocalStorage 都是在 HTML5 才提出来的存储⽅案,SessionStorage 主要⽤于 临时保存 同⼀窗⼝(或标签⻚)的数据,刷新⻚⾯时不会删除,关闭窗⼝或标签⻚之后将会删除这些数据。

👉 SessionStorage 的特点有以下:

  • sessionStorage 的数据只存在于当前浏览器的标签页
  • 数据在页面刷新后依然存在,但在关闭浏览器标签页之后数据就会被清除
  • localStorage 拥有统一的 API 接口
  • 对数据的操作是同步的

👉 SessionStorage 与 LocalStorage 对⽐:

  • SessionStorage 和 LocalStorage 都在本地进⾏数据存储
  • SessionStorage 也有同源策略的限制,但是 SessionStorage 有⼀条更加严格的限制, SessionStorage 只有在同⼀浏览器的同⼀窗⼝下才能够共享
  • LocalStorage 和 SessionStorage 都不能被爬⾍爬取

👉 SessionStorage 具体使用:

// 通过 setItem() 增加一个数据项
sessionStorage.setItem("myName", "zhangsan");

// 通过 getItem() 获取某个数据项
let me = sessionStorage.getItem("myName");

// 通过 removeItem() 移除某个数据项
sessionStorage.removeItem("myName");

// 移除所有数据项
sessionStorage.clear();

👉 SessionStorage 的使用场景:

  • 由于 SessionStorage具有时效性,所以可以⽤来存储⼀些⽹站的游客登录的信息,还有临时的浏览记录的信息。当关闭⽹站之后,这些信息也就随之消除了。

# 三、Web SQL

Web SQL 数据库 API 实际上不是 HTML5 规范的一部分,而是一个单独的规范,它引入了一组 API 来使用 SQL 来操作客户端数据库。

不过需要注意的是,HTML5 已经放弃 Web SQL 数据库

👉 Web SQL Database 规范中定义的三个核心方法,如下

  • openDatabase,这个方法使用现有数据库或新建数据库来创建数据库对象
  • transaction,这个方法允许我们根据情况控制事务的提交或回滚
  • executeSql,这个方法用于执行真实的 SQL 语句

👉 Web SQL 的特点(相比 CookieWebStorage 而言)

  • Web SQL 能方便进行对象存储
  • Web SQL 支持事务,能方便地进行数据查询和数据处理操作

👉 具体使用方式如下

var db = openDatabase("mydb", "1.0", "Test DB", 2 * 1024 * 1024);

db.transaction(function(tx) {
  // 执行查询操作
  tx.executeSql("CREATE TABLE IF NOT EXISTS LOGS (id unique, log)");
  // 执行插入操作
  tx.executeSql('INSERT INTO LOGS (id, log) VALUES (1, "foobar")');
  tx.executeSql('INSERT INTO LOGS (id, log) VALUES (2, "logmsg")');
});

# 四、IndexedDB

👉 详细内容可参考 浏览器数据库 IndexedDB 入门教程

现有的浏览器数据储存方案,都不适合储存大量数据:Cookie 的大小不超过 4KB,且每次请求都会发送回服务器;LocalStorage 在 2.5MB 到 10MB 之间(各家浏览器不同),而且不提供搜索功能,不能建立自定义的索引。所以,需要一种新的解决方案,这就是 IndexedDB 诞生的背景。

IndexedDB 是一种底层 API,用于客户端存储大量结构化数据,包括文件、二进制大型对象,该 API 使用索引来实现对该数据的高性能搜索。

👉 它的特点有以下这些:

  • 键值对存储,IndexedDB 内部采用对象仓库(Object Store)存放数据,所有类型的数据都可以直接存入,包括 JavaScript 对象,对象仓库中,数据以键值对的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误
  • 数据操作是异步的,IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的,异步设计是为了防止大量数据的读写,拖慢网页的表现
  • 存储空间大,IndexedDB 的储存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有上限
  • 支持二进制存储,IndexedDB 不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象)
  • 同源限制,IndexedDB 受到同源限制,每一个数据库对应创建它的域名,网页只能访问自身域名下的数据库,而不能访问跨域的数据库
  • 支持事务型,IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况

👉 indexedDB 的基本使用:

var dbName = "my_db";

var request = indexedDB.open(dbName, 2);

request.onerror = function(event) {
  // 错误处理
};

request.onupgradeneeded = function(event) {
  var db = event.target.result;

  // 建立一个对象仓库来存储我们客户的相关信息,我们选择 ssn 作为键路径(key path),因为 ssn 可以保证是不重复的
  var objectStore = db.createObjectStore("customers", { keyPath: "ssn" });

  // 建立一个索引来通过姓名来搜索客户,名字可能会重复,所以我们不能使用 unique 索引
  objectStore.createIndex("name", "name", { unique: false });

  // 使用邮箱建立索引,我们确保客户的邮箱不会重复,所以我们使用 unique 索引
  objectStore.createIndex("email", "email", { unique: true });

  // 使用事务的 oncomplete 事件确保在插入数据前对象仓库已经创建完毕
  objectStore.transaction.oncomplete = function(event) {
    // 将数据保存到新创建的对象仓库
    var customerObjectStore = db
      .transaction("customers", "readwrite")
      .objectStore("customers");
    customerData.forEach(function(customer) {
      customerObjectStore.add(customer);
    });
  };
};

# 总结

该篇文章主要梳理了浏览器本地存储的五种方案的使用,分别是CookieLocalStorageSessionStorageWeb SQLIndexedDB

image-20220409173222751

# 文章参考

Local Storage vs. Session Storage vs. Cookies

各种浏览器存储方式总结

浏览器数据库 IndexedDB 入门教程