官方解释:IndexedDB 是一种底层 API,用于在客户端存储大量的结构化数据(也包括文件/二进制大型对象(blobs))。该 API 使用索引实现对数据的高性能搜索。虽然 Web Storage 在存储较少量的数据很有用,但对于存储更大量的结构化数据来说力不从心。而 IndexedDB 提供了这种场景的解决方案。
IndexedDB提供了一套完整的API,包括打开数据库连接,创建数据库对象,增删改查等,然而这些API当中存在部分异步、部分同步的情况,异步接口并非Promise形式,操作起来稍有点复杂,相比localStorage来说,于是我对常用的一些方法进行了二次封装。
创建
创建一个数据库操作对象,并返回一个Object对象
数据库版本默认为: 1
/**
* 创建一个数据库连接
* @param {String} dbName 数据库名
* @param {Number} version 数据库版本
*/
function createIndexDB(dbName, version = 1) {
if (!dbName) {
throw new Error('dbName is required.')
}
let db
return {}
}
由于原生的IndexDB操作大部分是属于异步请求事件,则用Promise封装一个公共的方法
const createAsyncRequest = (request, options) => {
return new Promise((resolve, reject) => {
request.onsuccess = function (e) {
res = e.target.result
resolve(res)
}
request.onerror = function (e) {
reject(new Error(e.target.error))
}
})
}
打开
打开数据库,不存在时会默认创建这个数据库,返回Promise
/**
* 创建一个数据库连接
* @param {String} dbName 数据库名
* @param {Number} version 数据库版本
*/
function createIndexDB(dbName, version = 1) {
if (!dbName) {
throw new Error('dbName is required.')
}
let db
return {
open() {
return new Promise((resolve, reject) => {
const request = window.indexedDB.open(dbName, version)
request.onsuccess = function (e) {
db = e.target.result
resolve(db)
}
request.onerror = function (e) {
reject(new Error(e.target.error))
}
// 数据库创建或升级的时触发
request.onupgradeneeded = function (e) {
db = e.target.result
resolve(db)
}
})
},
}
}
关闭
关闭数据库
function close() {
const request = db.open(dbName)
return createAsyncRequest(request)
}
创建仓库对象
创建仓库数据对象(表) [同步]
function createObjectStore(storeName, options = {}) {
const _options = {
keyPath: 'id',
...options
}
try {
if (!db.objectStoreNames.contains(storeName)) {
let objectStore = db.createObjectStore(storeName, _options)
return objectStore
} else {
throw new Error(`The storeName <${storeName}> is exist.`)
}
} catch (e) {
throw new Error(e.target.error)
}
}
删除仓库对象
删除仓库数据对象(表) [同步]
function deleteObjectStore(storeName) {
try {
if (!db.objectStoreNames.contains(storeName)) {
return db.deleteObjectStore(storeName)
} else {
throw new Error(`The storeName <${storeName}> is not exist.`)
}
} catch (e) {
throw new Error(e.target.error)
}
}
创建索引
基于仓库数据对象创建索引
// 创建索引 [同步]
function createIndex(objectStore, options = {}) {
const {indexName, keyPath, objectParameters} = options
try {
if (objectStore) {
return objectStore.createIndex(indexName, keyPath, objectParameters)
} else {
throw new Error(`The objectStore is required.`)
}
} catch (e) {
throw new Error(e.target.error)
}
}
删除索引
基于仓库数据对象删除索引
// 删除索引 [同步]
function deleteIndex(objectStore, indexName) {
try {
if (objectStore) {
return objectStore.deleteIndex(indexName)
} else {
throw new Error(`The objectStore is required.`)
}
} catch (e) {
throw new Error(e.target.error)
}
}
操作数据对象
由于增删改查数据都需要操作仓库数据对象,则可将其封装成公共方法
function transactionObjectStore(storeName, options) {
const {model = 'readwrite', oncomplete, onerror} = options
try {
const transaction = db.transaction([storeName], model)
transaction.oncomplete = oncomplete
transaction.onerror = function(e) {
onerror && onerror(e)
throw new Error(e.target.error)
}
const objectStore = transaction.objectStore(storeName)
return objectStore
} catch (e) {
throw new Error(e.target.error)
}
}
增
基于仓库数据对象插入新数据
function add(storeName, data) {
let request = transactionObjectStore(storeName).add(data)
return createAsyncRequest(request)
}
删
基于仓库数据对象删除数据
function delete(storeName, key) {
let request = transactionObjectStore(storeName).delete(key)
return createAsyncRequest(request)
}
改
基于仓库数据对象更新数据
function put(storeName, data) {
let request = transactionObjectStore(storeName).put(data)
return createAsyncRequest(request)
}
查
基于仓库数据对象查询数据
function get(storeName, key) {
let request = transactionObjectStore(storeName, {model: 'readonly'}).get(key)
return createAsyncRequest(request)
}
查-所有
基于仓库数据对象查询所有数据
function getAll(storeName, query, count) {
let request = transactionObjectStore(storeName).getAll(query, count)
return createAsyncRequest(request)
}
查-条目数
基于仓库数据对象查询条目数
function count(storeName, query) {
let request = transactionObjectStore(storeName).count(query)
return createAsyncRequest(request)
}
代码清单
// util.indexDB.js
/**
* 创建一个数据库连接
* @param {Object} options 选项
*/
function createIndexDB(dbName, version = 1) {
if (!dbName) {
throw new Error('dbName is required.')
}
let db
const createAsyncRequest = (request, options) => {
return new Promise((resolve, reject) => {
request.onsuccess = function (e) {
res = e.target.result
resolve(res)
}
request.onerror = function (e) {
reject(new Error(e.target.error))
}
})
}
return {
// 打开数据库,不存在时会默认创建
open() {
return new Promise((resolve, reject) => {
const request = window.indexedDB.open(dbName, version)
request.onsuccess = function (e) {
db = e.target.result
resolve(db)
}
request.onerror = function (e) {
reject(new Error(e.target.error))
}
// 数据库创建或升级的时触发
request.onupgradeneeded = function (e) {
db = e.target.result
resolve(db)
}
})
},
// 关闭数据库
close() {
const request = db.open(dbName)
return createAsyncRequest(request)
},
// 创建仓库对象(表) [同步]
createObjectStore(storeName, options = {}) {
const _options = {
keyPath: 'id',
...options
}
try {
if (!db.objectStoreNames.contains(storeName)) {
let objectStore = db.createObjectStore(storeName, _options)
return objectStore
} else {
throw new Error(`The storeName <${storeName}> is exist.`)
}
} catch (e) {
throw new Error(e.target.error)
}
},
// 删除仓库对象(表) [同步]
deleteObjectStore(storeName) {
try {
if (!db.objectStoreNames.contains(storeName)) {
return db.deleteObjectStore(storeName)
} else {
throw new Error(`The storeName <${storeName}> is not exist.`)
}
} catch (e) {
throw new Error(e.target.error)
}
},
// 创建索引 [同步]
createIndex(objectStore, options = {}) {
const {indexName, keyPath, objectParameters} = options
try {
if (objectStore) {
return objectStore.createIndex(indexName, keyPath, objectParameters)
} else {
throw new Error(`The objectStore is required.`)
}
} catch (e) {
throw new Error(e.target.error)
}
},
// 删除索引 [同步]
deleteIndex(objectStore, indexName) {
try {
if (objectStore) {
return objectStore.deleteIndex(indexName)
} else {
throw new Error(`The objectStore is required.`)
}
} catch (e) {
throw new Error(e.target.error)
}
},
// 操作数据对象
transactionObjectStore(storeName, options) {
const {model = 'readwrite', oncomplete, onerror} = options
try {
const transaction = db.transaction([storeName], model)
transaction.oncomplete = oncomplete
transaction.onerror = function(e) {
onerror && onerror(e)
throw new Error(e.target.error)
}
const objectStore = transaction.objectStore(storeName)
return objectStore
} catch (e) {
throw new Error(e.target.error)
}
},
// 插入数据
add(storeName, data) {
let request = transactionObjectStore(storeName).add(data)
return createAsyncRequest(request)
},
// 查询数据
get(storeName, key) {
let request = transactionObjectStore(storeName, {model: 'readonly'}).get(key)
return createAsyncRequest(request)
},
// 查询数据
getAll(storeName, query, count) {
let request = transactionObjectStore(storeName).getAll(query, count)
return createAsyncRequest(request)
},
// 更新数据
put(storeName, data) {
let request = transactionObjectStore(storeName).put(data)
return createAsyncRequest(request)
},
// 删除数据
delete(storeName, key) {
let request = transactionObjectStore(storeName).delete(key)
return createAsyncRequest(request)
},
// 清除数据
clear(storeName) {
let request = transactionObjectStore(storeName).clear()
return createAsyncRequest(request)
},
// 条目数
count(storeName, query) {
let request = transactionObjectStore(storeName).count(query)
return createAsyncRequest(request)
},
}
}