一、概述
数据存储,可以是临时存储,也可以是永久存储,接下来我们一起看看在Web中,有哪些方式可以存储数据。
二、Cookie
三、Web Storage
1、概述
Web Storage 是HTML5新增的一种数据存储机制,随着浏览器对HTML5的支持度不断增加,在新建的项目中基本已经取代了cookie(它是服务器保存在浏览器的一小段文本信息,每个Cookie的大小一般不能超过4KB,超过这个长度的Cookie,将被忽略,不会被设置)。Web Storage更像是cookie的强化版,能够动用大得多的存储空间。Web Storage存储机制包含 会话存储 和 本地存储 这两个对象。它们存储值的方式和JavaScript中对象属性储存值的方式一样,都是以“键值对”存在的。
2、本地存储 & 会话存储
sessionStorage
:会话存储(临时存储),数据在浏览器关闭后会被清除localStorage
:本地存储,数据一直存在于浏览器中,除非调用clear
清除,一般用于数据持久化存储
会话存储和本地存储常用的方法:
setItem(key, val)
:存储数据 & 修改数据getItem(key)
:获取数据removeItem(key)
:移除数据clear()
:清空数据
提示:
- 不管是sessionStorage还是localStorage,他们的本质都是对象,所以我们可以通过点语法的形式对它们进行增删改查。
- 在浏览器调试工具的 Application 选项中可查看存储数据的可视化形式。
3、对象存储
Webstorage 不能直接存储对象类型的数据,需将对象类型的数据转换成 JSON
数据之后进行存储,读取的时候需进行解析。
// 1. 存
sessionStorage.usr = JSON.stringify({
name: "木子李",
age: 31,
major: "软件技术"
});
// 2. 取
JSON.parse(sessionStorage.usr);
4. Cookie vs Web Storage
-
存登录信息,大小限制为4KB左右
-
localStorage是Html5新增的,用于本地数据存储,保存的数据没有过期时间,一般浏览器大小限制在5MB
-
sessionStorage接口方法和localStorage类似,但保存的数据的只会在当前会话中保存下来,页面关闭后会被清空。
# | 生命期 | 大小限制 | 与服务器通信 |
---|---|---|---|
cookie | 一般由服务器生成,可设置失效时间。如果在浏览器端生成Cookie,默认是关闭浏览器后失效。 | 4KB | 每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题。 |
localStorage | 除非被清除,否则永久保存 | 5MB | 仅在浏览器中保存,不与服务器通信 |
sessionStorage | 仅在当前会话下有效,关闭页面或浏览器被清除 | 5MB | 仅在浏览器中保存,不与服务器通信 |
四、IndexDB
1、概述
随着浏览器的功能不断增强,越来越多的网站开始考虑,将大量数据储存在客户端,这样可以减少从服务器获取数据,直接从本地获取数据。现有的浏览器数据储存方案,都不适合储存大量数据:Cookie 的大小不超过4KB,且每次请求都会发送回服务器;LocalStorage 在5MB(各家浏览器不同),而且不提供搜索功能,不能建立自定义的索引。所以,需要一种新的解决方案,这就是 IndexedDB 诞生的背景。
通俗地说,IndexedDB 就是浏览器提供的本地数据库,它可以被网页脚本创建和操作。IndexedDB 允许储存大量数据,提供查找接口,还能建立索引。这些都是 LocalStorage 所不具备的。就数据库类型而言,IndexedDB 不属于关系型数据库(不支持 SQL 查询语句),更接近 NoSQL 数据库。
IndexedDB 具有以下特点:
- 对象存储;
- 异步读写;
- 支持事务;
- 同源限制;
- 存储空间大,接近无限制;
- 支持二进制存储;
2、基本概念
操作对象:
- 数据库:
IDBDatabase
- 对象仓库:
IDBObjectStore (表)
- 索引:
IDBIndex
- 事务:
IDBTransaction
- 操作请求:
IDBRequest
- 指针:
IDBCursor
- 主键集合:
IDBKeyRange
3、操作
3.1. 新建数据库
新建数据库与打开数据库是同一个操作。如果指定的数据库不存在,就会新建。不同之处在于,后续的操作主要在upgradeneeded
事件的监听函数里面完成,因为这时版本从无到有,所以会触发这个事件。
/**
* 获取数据库
* @param {*} databaseName 数据库名称
* @param {*} version 数据库版本
* @returns
*/
function openDB(databaseName, version) {
return new Promise((resolve, reject) => {
const DBOpenRequest = window.indexedDB.open(databaseName, version);
// 打开异常
DBOpenRequest.onerror = (e) => {
reject(e);
};
// 打开成功
DBOpenRequest.onsuccess = (e) => {
resolve(DBOpenRequest.result);
};
// 数据库升级事件,如果指定的版本号,大于数据库的实际版本号,就会发生数据库升级事件
DBOpenRequest.onupgradeneeded = (e) => {
console.log("Upgrading...");
};
});
}
调用
(async function () {
// 1. 打开数据库
const db = await openDB("TEST-DATABASE");
console.log(db);
})();
提示:如果浏览器已经存在该数据库,则直接返回,没如果不存在,则新建数据库之后返回。
3.2. 新建对象仓库(表)和索引
通常,新建数据库以后,第一件事是新建对象仓库(即新建表)。接下来我们在 onupgradeneeded
事件回调中新建一个仓库用于存储用户信息,并为其创建索引:
DBOpenRequest.onupgradeneeded = (e) => {
console.log('Upgrading...');
let db = e.target.result;
// 新建对象仓库(用户表)
if (!db.objectStoreNames.contains('USRS')) {
// 创建USRS对象仓库/主键为自增整数
// 如果需要指定值为主键,则可以设置:{keyPath: "键" }
let objStore = db.createObjectStore('USRS', {
keyPath: 'id',
autoIncrement: 'id',
});
// 创建索引,语法形式:objStore.createIndex(名称, 索引, 配置对象)
objStore.createIndex('索引', 'id', { unique: true });
objStore.createIndex('姓名', 'name', { unique: false });
objStore.createIndex('性别', 'gender', { unique: false });
objStore.createIndex('专业', 'major', { unique: false });
}
};
4.3. 添加记录
const request = db
.transaction(['USRS'], 'readwrite')
.objectStore('USRS')
.add({
name: '李四',
gender: '女',
major: '电子商务',
});
request.onsuccess = (e) => {
console.log('Add success!');
};
request.onerror = (e) => {
console.log(e);
};
上面代码中,写入数据需要新建一个事务。新建时必须指定表格名称和操作模式("只读"或"读写")。新建事务以后,通过IDBTransaction.objectStore(name)
方法,拿到 IDBObjectStore 对象,再通过表格对象的add()
方法,向表格写入一条记录。
写入操作是一个异步操作,通过监听连接对象的success
事件和error
事件,了解是否写入成功。
可以看到,USRS
表中已经插入了2条记录。
4.4. 获取记录
const request = db.transaction(['USRS']).objectStore('USRS').get(1);
request.onsuccess = (e) => {
console.log(request.result);
};
request.onerror = (e) => {
console.log(e);
};
// {name: '张三', gender: '男', major: '软件技术', id: 1}
4.5. 遍历记录
遍历数据表格的所有记录,要使用指针对象 IDBCursor
。
const objectStore = db.transaction('USRS').objectStore('USRS');
const res = [];
objectStore.openCursor().onsuccess = function (event) {
var cursor = event.target.result;
if (cursor) {
res.push(cursor.value);
cursor.continue();
} else {
console.log(res);
console.log('No more entries!');
}
};
0: {name: '张三', gender: '男', major: '软件技术', id: 1}
1: {name: '李四', gender: '女', major: '电子商务', id: 2}
length: 2
4.6. 更新记录
// 获取对象仓库
const objectStore = db
.transaction(['USRS'], 'readwrite')
.objectStore('USRS');
// 查询要修改的数据
const request = objectStore.get(1);
request.onsuccess = (e) => {
const data = e.target.result;
data.id = 1;
data.major = '网络工程';
// 执行更新
const putRequest = objectStore.put({ ...data });
putRequest.onsuccess = (e) => {
console.log('Put success!');
};
putRequest.onerror = (e) => {
console.log(e);
};
};
可以看见, id
为 1
的那条记录,major
字段成功修改成 “网络工程”。
3.7. 删除记录
db.transaction(['USRS'], 'readwrite').objectStore('USRS').delete(2);
删除 id
值为 2
的那条数据。
3.8. 使用索引
索引的意义在于,可以让你搜索任意字段,也就是说从任意字段拿到数据记录。如果不建立索引,默认只能搜索主键(即从主键取值)。
接下来,我们通过索引 姓名
查找数据:
const request = db
.transaction(['USRS'], 'readwrite')
.objectStore('USRS')
.index('姓名')
.get('张三');
request.onsuccess = function (e) {
var result = e.target.result;
console.log(result);
};
{name: '张三', gender: '男', major: '网络工程', id: 1}
提示:由于在开始我们创建索引时,名称使用的是中文,所以这里根据索引查找数据时,也应该填写中文。实际开发中,还是建议使用英文作为索引名称。