常见存储方式主要有两种:cookie、webStorage(localStorage和sessionStorage),但它们不适合储存大量数据:Cookie 的大小不超过4KB,且每次请求都会发送回服务器;LocalStorage 在 2.5MB 到 10MB 之间(各家浏览器不同),而且不提供搜索功能,不能建立自定义的索引。因此,我们可以使用前端数据库来实现。在浏览器上有两种数据库:webSQL和IndexedDB。
IndexedDB是浏览器提供的本地数据库, 允许储存大量数据,提供查找接口,还能建立索引。这些都是 LocalStorage 所不具备的。就数据库类型而言,IndexedDB 不属于关系型数据库(不支持 SQL 查询语句),更接近 NoSQL 数据库。
IndexedDB 具有以下特点:
键值对储存。 IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript对象。对象仓库中,数据以"键值对"的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。
异步。IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。
支持事务。 IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。
同源限制 IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。
储存空间大 IndexedDB 的储存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有上限。
支持二进制储存。 IndexedDB 不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象)。
IndexedDB的一些基本概念:
数据库,数据库是一系列相关数据的容器。每个域名(严格的说,是协议 + 域名 + 端口)都可以新建任意多个数据库。 IndexedDB数据库有版本的概念。同一个时刻,只能有一个版本的数据库存在。如果要修改数据库结构(新增或删除表、索引或者主键),只能通过升级数据库版本完成。
对象仓库,每个数据库包含若干个对象仓库(object store)。它类似于关系型数据库的表格。
数据记录,对象仓库保存的是数据记录。每条记录类似于关系型数据库的行,但是只有主键和数据体两部分。主键可以是数据记录里面的一个属性,也可以指定为一个递增的整数编号。
索引,添加索引后可以使用索引查询数据
事务,数据记录的读写和删改,都要通过事务完成。事务对象提供error、abort和complete三个事件,用来监听操作结果。
通过类来封装可以提高可读性、 可拓展性、可维护性强
// 数据库对象
class MyDataBase {
constructor(
database,
version,
tablename
) {
this.DATABASE_NAME = database;
this.VERSION = version;
this.TABLE = tablename;
}
// 相关数据库操作
...
}
连接并初始化数据库:
/**
* 获取数据库对象
*/
getDB(dbName, version, table) {
return new Promise(function (resolve, reject) {
let request = window.indexedDB.open(dbName, version);
request.onsuccess = function (e) {
// let db = e.target.result;
let db = this.result;
resolve(db);
}
request.onupgradeneeded = function (e) {
// 初始化数据库
let db = this.result;
if (!db.objectStoreNames.contains(table || this.TABLE)) {
var objectStore = db.createObjectStore(table || this.TABLE, {
autoIncrement: true
});
// 创建索引
// 可以通过索引查询
objectStore.createIndex("name", "name", {
unique: true
});
objectStore.createIndex("email", "email", {
unique: true
});
};
}
})
}
获取数据库中某一张表的所有数据记录:
/**
* 读取 [db]--[tableName] 下的所有记录
*/
readAllRecord(db, tableName) {
return new Promise(function (resolve, reject) {
let objectStore = db.transaction(tableName).objectStore(tableName);
let records = [];
objectStore.openCursor().onsuccess = function (e) {
let cursor = e.target.result;
if (cursor) {
records.push(cursor.value);
cursor.continue();
} else {
resolve({
code: 200,
data: records
})
}
}
objectStore.openCursor().onerror = function (e) {
reject({
code: 0,
msg: e.target.error
})
}
})
}
查找某一条数据(根据数据库表的主键):
/**
* 获取 [db]--[tableName]下,主键为 [key] 的某条记录
*/
getRecord(db, tableName, key) {
return new Promise(function (resolve, reject) {
let request = db.transaction(tableName)
.objectStore(tableName)
.get(key);
request.onsuccess = function (e) {
resolve({
code: 200,
data: e.target.result
});
}
request.onerror = function (e) {
reject({
code: 0,
msg: e.target.error
})
}
})
}
根据索引查找数据:
/**
* 根据索引所在位置 [_index],获取 [db]--[tableName]下,索引值为 [identify]的某条记录
*/
getRecordWithIndex(db, tableName, _index, identify) {
return new Promise(function (resolve, reject) {
let request = db.transaction(tableName, "readwrite")
.objectStore(tableName)
.index(_index)
.get(identify);
request.onsuccess = function (e) {
resolve({
code: 200,
data: e.target.result
});
}
request.onerror = function (e) {
reject({
code: 0,
msg: e.target.error
})
}
})
}
向数据库特定表添加一条数据:
/**
* 在 [db]--[tableName]中新增一条记录[data]
*/
addRecord(db, tableName, data) {
return new Promise(function (resolve, reject) {
let request = db.transaction(tableName, "readwrite")
.objectStore(tableName)
.add(data);
request.onsuccess = function () {
resolve({
code: 200
});
}
request.onerror = function (e) {
reject({
code: 0,
msg: e.target.error
})
}
})
}
修改某一条数据(根据主键):
/**
* 将 [db]--[tableName]中,主键为 [key]的数据为,更新为 [data]
*/
updateRecord(db, tableName, data, key) {
return new Promise(function (resolve, reject) {
let request = db.transaction(tableName, "readwrite")
.objectStore(tableName)
.put(data, key);
request.onsuccess = function () {
resolve({
code: 200
})
}
request.onerror = function (e) {
reject({
code: 0,
msg: e.target.error
})
}
})
}
删除某一条数据(根据主键):
/**
* 在 [db]--[tableName]下,删除主键为[key]的记录
*/
deleteRecord(db, tableName, key) {
return new Promise(function (resolve, reject) {
let request = db.transaction(tableName, "readwrite")
.objectStore(tableName)
.delete(key);
request.onsuccess = function () {
resolve({
code: 200
});
}
request.onerror = function (e) {
reject({
code: 0,
msg: e.target.error
})
}
})
}
完整代码:
// 数据库对象
class MyDataBase {
constructor(
database,
version,
tablename
) {
this.DATABASE_NAME = database;
this.VERSION = version;
this.TABLE = tablename;
}
/**
* 在 [db]--[tableName]中新增一条记录[data]
*/
addRecord(db, tableName, data) {
return new Promise(function (resolve, reject) {
let request = db.transaction(tableName, "readwrite")
.objectStore(tableName)
.add(data);
request.onsuccess = function () {
resolve({
code: 200
});
}
request.onerror = function (e) {
reject({
code: 0,
msg: e.target.error
})
}
})
}
/**
* 获取 [db]--[tableName]下,主键为 [key] 的某条记录
*/
getRecord(db, tableName, key) {
return new Promise(function (resolve, reject) {
let request = db.transaction(tableName)
.objectStore(tableName)
.get(key);
request.onsuccess = function (e) {
resolve({
code: 200,
data: e.target.result
});
}
request.onerror = function (e) {
reject({
code: 0,
msg: e.target.error
})
}
})
}
/**
* 根据索引所在位置 [_index],获取 [db]--[tableName]下,索引值为 [identify]的某条记录
*/
getRecordWithIndex(db, tableName, _index, identify) {
return new Promise(function (resolve, reject) {
let request = db.transaction(tableName, "readwrite")
.objectStore(tableName)
.index(_index)
.get(identify);
request.onsuccess = function (e) {
resolve({
code: 200,
data: e.target.result
});
}
request.onerror = function (e) {
reject({
code: 0,
msg: e.target.error
})
}
})
}
/**
* 读取 [db]--[tableName] 下的所有记录
*/
readAllRecord(db, tableName) {
return new Promise(function (resolve, reject) {
let objectStore = db.transaction(tableName).objectStore(tableName);
let records = [];
objectStore.openCursor().onsuccess = function (e) {
let cursor = e.target.result;
if (cursor) {
records.push(cursor.value);
cursor.continue();
} else {
resolve({
code: 200,
data: records
})
}
}
objectStore.openCursor().onerror = function (e) {
reject({
code: 0,
msg: e.target.error
})
}
})
}
/**
* 将 [db]--[tableName]中,主键为 [key]的数据为,更新为 [data]
*/
updateRecord(db, tableName, data, key) {
return new Promise(function (resolve, reject) {
let request = db.transaction(tableName, "readwrite")
.objectStore(tableName)
.put(data, key);
request.onsuccess = function () {
resolve({
code: 200
})
}
request.onerror = function (e) {
reject({
code: 0,
msg: e.target.error
})
}
})
}
/**
* 在 [db]--[tableName]下,删除主键为[key]的记录
*/
deleteRecord(db, tableName, key) {
return new Promise(function (resolve, reject) {
let request = db.transaction(tableName, "readwrite")
.objectStore(tableName)
.delete(key);
request.onsuccess = function () {
resolve({
code: 200
});
}
request.onerror = function (e) {
reject({
code: 0,
msg: e.target.error
})
}
})
}
/**
* 获取数据库对象
*/
getDB(dbName, version, table) {
return new Promise(function (resolve, reject) {
let request = window.indexedDB.open(dbName, version);
request.onsuccess = function (e) {
// let db = e.target.result;
let db = this.result;
resolve(db);
}
request.onupgradeneeded = function (e) {
// 初始化数据库
let db = this.result;
if (!db.objectStoreNames.contains(table || this.TABLE)) {
var objectStore = db.createObjectStore(table || this.TABLE, {
autoIncrement: true
});
// 创建索引
// 可以通过索引查询
objectStore.createIndex("name", "name", {
unique: true
});
objectStore.createIndex("email", "email", {
unique: true
});
};
}
})
}
}
// 数据库连接信息
const connection = {
database: 'my_database',
version: '1.0',
tablename: 'table_user'
}
// 获取数据库对象
const database = new MyDataBase(
connection.database,
connection.version,
connection.tablename
);
database.getDB(connection.database, connection.version, connection.tablename).then(db => {
let data = {
id: 1,
name: "张三",
age: 18,
email: "[email protected]"
}
let data1 = {
id: 2,
name: "张三22",
age: 18,
email: "[email protected]"
}
database.addRecord(db, connection.tablename, data).then(res => {
console.log(res);
}, err => {
console.log(err);
})
database.addRecord(db, connection.tablename, data1).then(res => {
console.log(res);
}, err => {
console.log(err);
})
database.readAllRecord(db, connection.tablename).then(res => {
console.log(res);
}, err => {
console.log(err);
})
// 根据数据库存储数据的主键来查找某一条数据
database.getRecord(db, connection.tablename, 1).then(res => {
console.log(res);
}, err => {
console.log(err);
})
// 根据索引为email字段,并且索引值为[email protected] 来查找数据
database.getRecordWithIndex(db, connection.tablename, "email", "[email protected]").then(res => {
console.log(res);
}, err => {
console.log(err);
})
database.updateRecord(db, connection.tablename, { id: 1, name: "李四", age: 18, email: "[email protected]" }, 1).then(res => {
console.log(res);
}, err => {
console.log(err);
})
database.deleteRecord(db, connection.tablename, 1).then(res => {
console.log(res);
}, err => {
console.log(err);
})
})