浅谈浏览器存储
1、概念
浏览器存储 :浏览器存储是指浏览器提供的一种本地存储数据的机制,包括:Cookie
、Web Storage
、IndexedDB
等。
前端持久化存储 :是指通过 JavaScript
在客户端进行数据持久化存储的方式,包括 LocalStorage
、IndexedDB
和 File API
等。
表格一览
特性 | cookie | localStorage | sessionStorage | indexDB |
---|---|---|---|---|
数据生命周期 | 一般由服务器生成,可以设置过期时间;前端采用和js-cookie等组件也可以生成 | 除非被清理,否则一直存在;浏览器关闭还会保存在本地,但是不支持跨浏览器 | 页面关闭就清理刷新依然存在,不支持跨页面交互 | 除非被清理,否则一直存在 |
数据存储大小 | 4K | 5M | 5M | 不限制大小 |
与服务端通信 | 每次都会携带在请求的header 中,对于请求性能有影响;同时由于请求中都带有,所以也容易出现安全问题 | 不参与 | 不参与 | 不参与 |
特点 | 字符串键值对在本地存储数据 | 字符串键值对在本地存储数据 | 字符串键值对在本地存储数据 | IndexedDB 是一个非关系型数据库(不支持通过 SQL 语句操作)。可以存储大量数据,提供接口来查询,还可以建立索引,这些都是其他存储方案无法提供的能力。 |
2、Cookie
Cookie
最开始被设计出来其实并不是来做本地存储的,而是为了弥补HTTP
在状态管理上的不足。
HTTP
协议是一个无状态协议,客户端向服务器发请求,服务器返回响应,故事就这样结束了,但是下次发请求如何让服务端知道客户端是谁呢?
这种背景下,就产生了 Cookie
.
Cookie
本质上就是浏览器里面存储的一个很小的文本文件,内部以键值对的方式来存储(在chrome开发者面板的Application
这一栏可以看到)。向同一个域名下发送请求,都会携带相同的 Cookie
,服务器拿到 Cookie
进行解析,便能拿到客户端的状态。它可以设置过期时间,用于在客户端和服务器之间传递数据。由于每次请求都会携带 Cookie
信息,可能导致网络开销增加,并且存在安全性问题,故不适合存储大量数据。
Cookie过期等配置
Cookie
分为:Session Cookie
和持久型 Cookie
。Cookie
设置中有个 HttpOnly
参数,前端浏览器使用document.cookie
是读取不到 HttpOnly
类型的 Cookie
的,被设置为 HttpOnly
的 Cookie
记录只能通过 HTTP
请求头发送到服务器端进行读写操作,这样就避免了服务器的 Cookie
记录被前端 javascript
修改,保证了服务器验证 Cookie
的安全性。
Cookie
的内容主要包括:名字,值,过期时间,路径和域。路径与域一起构成 Cookie
的作用范围。若不设置过期时间,则表示这个 Cookie
的生命期为浏览器会话期间,关闭浏览器窗口, Cookie
就消失。
这种生命期为浏览器会话期的 Cookie
被称为 会话Cookie
。会话Cookie
一般不存储在硬盘上而是保存在内存里。若设置了过期时间,浏览器就会把 Cookie
保存到硬盘上,关闭后再次打开浏览器,这些 Cookie
仍然有效直到超过设定的过期时间。存储在硬盘上的 Cookie
可以在不同的浏览器进程间共享,比如两个IE窗口。而对于保存在内存里的 Cookie
,不同的浏览器有不同的处理方式。
操作方式
js
// 设置Cookie:
function setCookie(name, value, days) {
let expires = "";
if (days) {
const date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
expires = "; expires=" + date.toUTCString();
}
document.cookie = name + "=" + value + expires + "; path=/";
}
// 读取Cookie
function getCookie(name) {
const nameEQ = name + "=";
const ca = document.cookie.split(';');
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) === ' ') {
c = c.substring(1, c.length);
}
if (c.indexOf(nameEQ) === 0) {
return c.substring(nameEQ.length, c.length);
}
}
return null;
}
// 删除Cookie
function deleteCookie(name) {
setCookie(name, "", -1);
}
// 使用
// 设置Cookie,有效期为7天
setCookie("username", "john_doe", 7);
console.log(cookie); // "username=john_doe; expires=Fri, 04 Aug 2023 14:47:16 GMT; path=/"
// 读取Cookie
const username = getCookie("username");
console.log(username); // "john_doe"
// 删除Cookie
deleteCookie("username");
Cookie
的作用很好理解,就是用来做状态存储的,但它也是有诸多致命的缺陷的:
- 容量缺陷。
Cookie
的体积上限只有4KB
,只能用来存储少量的信息。 - 性能缺陷。
Cookie
紧跟域名,不管域名下面的某一个地址需不需要这个Cookie
,请求都会携带上完整的Cookie
,这样随着请求数的增多,其实会造成巨大的性能浪费的,因为请求携带了很多不必要的内容。 - 安全缺陷。由于
Cookie
以纯文本的形式在浏览器和服务器中传递,很容易被非法用户截获,然后进行一系列的篡改,在Cookie
的有效期内重新发送给服务器,这是相当危险的。另外,在HttpOnly
为false
的情况下,Cookie
信息能直接通过JS
脚本来读取。
3、Session
session
机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。当程序需要为某个客户端的请求创建一个session
时,服务器首先检查这个客户端的请求里是否已包含了一个session
标识(称为 session ID
),如果已包含则说明以前已经为此客户端创建过 session
,服务器就按照 session ID
把这个session
检索出来使用(检索不到,会新建一个),如果客户端请求不包含 session ID
,则为此客户端创建一个session
并且生成一个与此session
相关联的 session ID
,session ID
的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个 session ID
将被在本次响应中返回给客户端保存。
总结
- 服务器端存储 :
session
是服务器端的一种机制,用于在服务端存储用户会话数据。服务器会为每个用户创建一个唯一的session
,并在客户端保存一个对应的session ID
。 - 生命周期 :
session
的生命周期由服务器管理,可以设置session
的过期时间,当用户长时间不活动或超过过期时间时,session
会被销毁。
Cookie 和 Session 比较
cookie
数据存放在客户的浏览器上,session
数据放在服务器上。cookie
不是很安全,别人可以分析存放在本地的cookie
并进行cookie
欺骗session
会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能- 单个
cookie
保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie
。 - 个人建议:将登录信息等重要信息存放在
session
中,其他信息如果需要在每个请求中携带,可以放在cookie
中,剩下的信息本地化缓在Web Storage
中。
4、Web Storage
Web Storage
包括 localStorage
和 sessionStorage
,是 HTML5
新增的浏览器存储机制。它们在浏览器端存储数据,不会随着每次请求发送到服务器,所以不会增加网络开销。localStorage
存储的数据没有过期时间,除非用户手动清除或网站清除,否则数据会一直存在。sessionStorage
的数据在用户关闭浏览器或页面后会被清除,适用于临时性数据存储。
4.1、localStorage
localStorage
有一点跟 Cookie
一样,就是针对一个域名,即在同一个域名下,会存储相同的一段。它可以存储较大量的数据,数据不会过期,一直保留在客户端。由于数据保存在客户端,所以不会增加服务器负担。单个 localStorage
的大小受限,可以用多个 iframe
方式使用多个域名来突破单个页面下 localStorage
存储数据的最大限制。
特别说明:浏览器多个标签页打开同个域名时,localStorage
内容一般是共享的。其位置这可以监听事件 storage
来做一致性操作响应处理。这样会导致如下现象:
标签页一:通过某行为修改
localStorage
中某个属性值,然后数据接口依赖该属性值;标签页二:由于
localStorage
标签页间共享,导致标签页二数据不准确!
操作方式
接下来我们来具体看看如何来操作 localStorage
。
js
let obj = { name: "sanyuan", age: 18 };
localStorage.setItem("name", "sanyuan");
localStorage.setItem("info", JSON.stringify(obj));
接着进入相同的域名时就能拿到相应的值:
js
let name = localStorage.getItem("name");
let info = JSON.parse(localStorage.getItem("info"));
从这里可以看出,localStorage
其实存储的都是字符串,如果是存储对象需要调用 JSON.stringify
方法,并且用 JSON.parse
来解析成对象。
应用场景
利用 localStorage
的较大容量和持久特性,可以利用 localStorage
存储一些内容稳定的资源,比如官网的 logo
,或者存储 Base64
格式的图片资源等。
Cookie 和 localStorage 比较
localStorage
的容量上限为5M
,相比于Cookie
的4K
大大增加。当然这个5M
是针对一个域名的,因此对于一个域名是持久存储的。- 只存在客户端,默认不参与与服务端的通信。这样就很好地避免了
Cookie
带来的性能问题 和安全问题。 - 接口封装。通过
localStorage
暴露在全局,并通过它的setItem
和getItem
等方法进行操作,非常方便。
4.2、sessionStorage
sessionStorage
、localStorage
、cookie
都是在浏览器端存储的数据,其中 sessionStorage
的概念很特别,引入了一个"浏览器窗口"
的概念。sessionStorage
是在同源的同窗口(或tab)中,始终存在的数据。也就是说只要这个浏览器窗口没有关闭,即使刷新页面或进入同源另一页面,数据仍然存在。关闭窗口后,sessionStorage
即被销毁。同时"独立"
打开的不同窗口,即使是同一页面,sessionStorage
对象也是不同的。
临时存储
很多时候数据只需要在用户浏览一组页面期间使用,关闭窗口后数据就可以丢弃了,这种情况使用 sessionStorage
非常方便。
sessionStorage
以下方面和 localStorage
一致:
- 容量。容量上限也为 5M。
- 只存在客户端,默认不参与与服务端的通信。
- 接口封装。除了
sessionStorage
名字有所变化,存储方式、操作方式均和localStorage
一样。
但 sessionStorage
和 localStorage
有一个本质的区别,那就是前者只是 会话级别的存储 ,并不是 持久化存储 。会话结束,也就是页面关闭,这部分 sessionStorage
就不复存在了。
操作方式
接下来我们来具体看看如何来操作 sessionStorage
。
js
//存储
sessionStorage.setItem("name", "sanyuan");
//取出
sessionStorage.getItem("name");
//清除
sessionStroage.clear();
应用场景
- 可以用它对表单信息进行维护,将表单信息存储在里面,可以保证页面即使刷新也不会让之前的表单信息丢失。
- 可以用它存储本次浏览记录。如果关闭页面后不需要这些记录,用
sessionStorage
就再合适不过了。事实上微博就采取了这样的存储方式。
5、IndexedDB
IndexedDB
是运行在浏览器中的非关系型数据库
,为大型数据的存储提供了接口。 本质上是数据库,绝不是和刚才 WebStorage
的 5M
一个量级,理论上这个容量是没有上限的。
关于它的使用,本文侧重原理,而且 MDN 上的教程文档已经非常详尽,这里就不做赘述了,感兴趣可以看一下使用文档。
IndexedDB的存储空间(所有访问的网站总和)为磁盘可用空间的50%,或根据浏览器的设定分配;
接着我们来分析一下 IndexedDB
的一些重要特性,除了拥有数据库本身的特性,比如支持事务
,存储二进制数据
,还有这样一些特性需要格外注意:
- 键值对存储。内部采用
对象仓库
存放数据,在这个对象仓库中数据采用键值对的方式来存储。 - 异步操作。数据库的读写属于
I/O
操作, 浏览器中对异步I/O
提供了支持。 - 受同源策略限制,即无法访问跨域的数据库。
- 它是
NoSQL
的,不需要我们去写一些特定的SQL
语句来对数据库进行操作,数据形式使用的json
。 - 一个数据库中可以包含多种对象集合,相对于
SQL
数据库来说就是多个表;在一个域(名)下,还可以有多个数据库。但是不能跨域访问别的域名之下的数据库。
另外一方面,用 localStorage
只能保存字符串,如果是其他的类型,那就必须用 JSON.stringify
来转换为字符串后再保存,而 IndexedDB
则可以直接保存。此外,还具备一般 DBMS
的常用功能,例如遍历、筛选等。
indexedDB出现的意义
- 前端存储,已经有了
localStorage
和Cookies
,但是它们都是比较简单的技术。而IndexedDB
提供了类似数据库风格的数据储存和使用方式。 Cookies
只能是字符串,储存空间有限,每次HTTP
接受和发送都会传递Cookies
数据,它会占用额外的流量。localStorage
是用key-value
键值模式储存数据,想让localstorage
存储对象,你需要借助JSON.stringify()
能将对象变成字符串形式,再用JSON.parse()
将字符串还原成对象,当存储的数据庞大时,这就不是最佳的方案了,localstorage
就是专门为小数量数据设计的,它的api
设计为同步的。IndexedDB
很适合存储大量数据,它的API
是异步调用的,IndexedDB
使用索引存储数据,各种数据库操作放在事务中执行,IndexedDB
支持简单的数据类型,它比localstorage
强大,API
也相对复杂,对于简单的数据,还是使用localstorage
。IndexedDB
能提供更为复杂的查询数据的方式。
IndexedDB特点
- 非关系型数据库(
NoSql
) :我们都知道MySQL
等数据库都是关系型数据库,它们的主要特点就是数据都以一张二维表的形式存储,而IndexedDB
是非关系型数据库,主要以键值对的形式存储数据。 - 持久化存储 :
cookie
、localStorage
、sessionStorage
等方式存储的数据当我们清楚浏览器缓存后,这些数据都会被清除掉的,而使用IndexedDB
存储的数据则不会,除非手动删除该数据库。 - 异步操作 :
IndexedDB
操作时不会锁死浏览器,用户依然可以进行其他的操作,这与localstorage
形成鲜明的对比,后者是同步的。 - 支持事务 :
IndexedDB
支持事务(transaction
),这意味着一系列的操作步骤之中,只要有一步失败了,整个事务都会取消,数据库回滚的事务发生之前的状态,这和MySQL
等数据库的事务类似。 - 同源策略 :
IndexedDB
同样存在同源限制,每个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。 - 存储容量大 :这也是
IndexedDB
最显著的特点之一了,这也是不用localStorage
等存储方式的最好理由。
操作方式
js
/**
* 打开数据库
* @param {object} dbName 数据库的名字
* @param {string} storeName 仓库名称
* @param {string} version 数据库的版本
* @return {object} 该函数会返回一个数据库实例
*/
function openDB(dbName, version = 1) {
return new Promise((resolve, reject) => {
// 兼容浏览器
var indexedDB =
window.indexedDB ||
window.mozIndexedDB ||
window.webkitIndexedDB ||
window.msIndexedDB;
let db;
// 打开数据库,若没有则会创建
const request = indexedDB.open(dbName, version);
// 数据库打开成功回调
request.onsuccess = function (event) {
db = event.target.result; // 数据库对象
console.log("数据库打开成功");
resolve(db);
};
// 数据库打开失败的回调
request.onerror = function (event) {
console.log("数据库打开报错");
};
// 数据库有更新时候的回调
request.onupgradeneeded = function (event) {
// 数据库创建或升级的时候会触发
console.log("onupgradeneeded");
db = event.target.result; // 数据库对象
var objectStore;
// 创建存储库
objectStore = db.createObjectStore("signalChat", {
keyPath: "sequenceId", // 这是主键
// autoIncrement: true // 实现自增
});
// 创建索引,在后面查询数据的时候可以根据索引查
objectStore.createIndex("link", "link", { unique: false });
objectStore.createIndex("sequenceId", "sequenceId", { unique: false });
objectStore.createIndex("messageType", "messageType", {
unique: false,
});
};
});
}
......
我们将创建数据库的操作封装成了一个函数,并且该函数返回一个 promise
对象,使得在调用的时候可以链式调用,函数主要接收两个参数:数据库名称、数据库版本。函数内部主要有三个回调函数,分别是:
onsuccess
:数据库打开成功或者创建成功后的回调,这里我们将数据库实例返回了出去。onerror
:数据库打开或创建失败后的回调。onupgradeneeded
:当数据库版本有变化的时候会执行该函数,比如我们想创建新的存储库(表),就可以在该函数里面操作,更新数据库版本即可。
应用场景
IndexedDB
是异步操作的,适合存储大量数据或需要离线访问的应用。- 数据可视化等界面,大量数据,每次请求会消耗很大性能。
- 即时聊天工具,大量消息需要存在本地。
- 其它存储方式容量不满足时,不得已使用
IndexedDB
。
6、其他存储
WebSQL
:二维表的形成存储大量数据到客户端,但目前只有Chrome
浏览器有。Application Cache
:通过manifest
配置文件在本地有选择性地存储javascript
、css
、图片等静态资源文件的文件缓存机制,已废弃。cacheStorage
:在ServiceWorker
规范中定义的,用于保存每个ServiceWorker
声明的Cache
对象,未来可能替代Application Cache
的离线方案。Flash缓存
:主要基于Flash
,具有读写浏览器本地目录的功能。File API
:File API
允许前端通过JavaScript
读取和操作用户的本地文件。可以实现将文件存储在客户端,并在需要时读取和处理文件数据。
7、区别
localStorage
:
- 存储容量较大 :
localStorage
可以存储较大量的数据,通常可以存储5MB
的数据。 - 持久性 :
localStorage
中的数据不会过期,除非用户手动清除或网站清除,否则数据会一直保留在客户端。 - 作用域 :
localStorage
的作用域是整个域名,即在同一个域名下的所有页面都可以访问同一份localStorage
数据。
sessionStorage
:
- 存储容量较大 :与
localStorage
一样,通常可以存储5MB
的数据。 - 会话级别 :
sessionStorage
中的数据在当前会话期间有效,即在用户关闭当前浏览器窗口或标签页后,数据会被清除。 - 作用域 :
sessionStorage
的作用域是当前窗口或标签页,不同窗口或标签页之间无法共享数据。
session
:
- 服务器端存储 :
session
是服务器端的一种机制,用于在服务端存储用户会话数据。服务器会为每个用户创建一个唯一的session
,并在客户端保存一个对应的session ID
。 - 生命周期 :
session
的生命周期由服务器管理,可以设置session
的过期时间,当用户长时间不活动或超过过期时间时,session
会被销毁。
cookies
:
- 存储容量小 :
cookies
的存储容量较小,一般限制在4KB
左右。 - 持久性 :可以设置
cookies
的过期时间,可以是会话级的,也可以是持久性的。当设置了过期时间,cookies
会在过期后被删除。 - 作用域 :
cookies
的作用域是整个域名,与localStorage
类似,在同一个域名下的所有页面都可以访问相同的cookies
数据。
IndexedDB
:
- 存储容量大 :
IndexedDB
是一种数据库,可以存储较大量的结构化数据,一般可以存储几十MB甚至几百MB的数据。 - 异步操作 :
IndexedDB
是异步的,操作数据时需要使用异步API
进行处理。 - 丰富的查询功能 :
IndexedDB
支持复杂的数据查询和索引,可以高效地检索和处理大量数据。
8、总结
localStorage
和sessionStorage
是HTML5
新增的浏览器存储机制,用于在客户端存储数据。session
是服务器端的存储机制,用于在服务端存储用户会话数据。cookies
是在客户端和服务端都可以操作的存储机制,用于存储少量数据。它可以设置过期时间,可以用于跟踪用户状态和实现记住登录功能。localStorage
、sessionStorage
、session
和cookies
是浏览器端的存储机制,用于在客户端存储数据,容量和持久性各有不同。IndexedDB
是一种高级的浏览器端数据库,适用于需要存储大量结构化数据并进行复杂查询的场景。它与其他浏览器存储方式相比,存储容量更大且查询效率更高,但使用起来相对复杂一些,需要异步操作。