随着客户的要求越来越高,浏览器的功能不断增强,越来越多的网站开始考虑,将大量数据储存在客户端,这样可以减少从服务器获取数据,直接从本地获取数据。
然而,现有的浏览器数据储存方案,都不适合储存大量数据、或者并不提供搜索功能,不能建立自定义的索引。所以,需要一种新的解决方案,这就是 IndexedDB 诞生的背景。
IndexedDB 是 HTML5离线存储数据的一种解决方案,是浏览器提供的本地数据库,它可以被网页脚本创建和操作。
IndexedDB 允许储存大量数据,使用索引高效搜索数据进行读写和管理操作。这些都是 LocalStorage 所不具备的。就数据库类型而言,IndexedDB 不属于关系型数据库(不支持 SQL 查询语句),更接近 NoSQL 数据库。
Cookies | Localstorage | SessionStorage | IndexedDB | |
---|---|---|---|---|
生命周期 | Expire设置时间 | 永久储存(可手动清除) | 会话级别 | 永久储存(可手动清除) |
储存数据大小 | 4K左右 | 5M左右 | 5M左右 | 无限储存 |
与服务器通信 | cookie 的数据自动传递到服务器,服务器端也可以写在cookie到服务端 | 不参与 | 不参与 | 不参与 |
易用性 | 自己封装方法 | 原生接口 | 原生接口 | 原生接口 |
1、键值对存储:内部采用对象仓库(Object Store)存放数据,所有类型都可以直接存入(包括javascript对象),每一个数据对象对应为唯一主键(不可重复)。
2、异步:防止大量数据的读写、拖慢网页
3、支持事务:只要有一步失败,整个transaction都取消,不存在只改写一部分数据的情况,安全性更高。
4、同源限制:每一个数据库都有他对应的域名,网页只能访问自身域名对应的数据库,不能跨域访问。
5、存储空间大:IndexedDB 的储存空间比 LocalStorage 大得多,理论上没有上限。
6、支持二进制存储:除了字符串之外,储存二进制数据(ArrayBuffer 对象和 Blob 对象)
类比sql型数据库,IndexedDB中的DB(数据库)就是sql中的DB,而Object Store(存储空间)则是数据表,Item则等于表中的一条数据对象(记录)
1、创建数据库
/* 调用示例
var IDB = new OpenIndexDB({
"dbname":"newindexdb",//数据库名称,必填
"dbversion":1,//数据库版本号 不填则默认 1
"store":"personss"//仓库名称 不填则默认 kmgBst
})
*/
/**
* [OpenIndexDB 创建一个本地数据库]
* @param {[Object]} opt [初始属性]
*/
function OpenIndexDB(opt) {
const $this = this
// 创建indexedDB对象,兼容各种浏览器
const indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.msIndexedDB
if (!indexedDB) {
console.log('indexDB创建失败:你的浏览器不支持IndexedDB')
return '创建失败'
}
if (!opt.dbname) {
console.log('indexDB创建失败:indexDB名称不能为空')
return '创建失败'
}
// 初始化属性
$this.dbInfo = {
name: opt.dbname, // 数据库名称
version: opt.dbversion || 1, // 数据库版本号
store: opt.store || 'kmgBst', // 仓库名称
db: null, // 数据库变量
key: opt.key || 'key' // 关键字
}
// 创建indexDB
$this.opendb()
}
2、创建indexDB
/**
* [opendb 创建indexDB]
*/
OpenIndexDB.prototype.opendb = function() {
// 定义indexDB的各项信息
const $this = this
const dbname = this.dbInfo.name
const dbversion = this.dbInfo.version
const storeName = this.dbInfo.store
const key = this.dbInfo.key
// 打开一个数据库
const request = indexedDB.open(dbname, dbversion)
// 打开失败
request.onerror = function(e) {
console.log(e.currentTarget.error.message)
}
// 打开成功!
request.onsuccess = function(e) {
$this.dbInfo.db = e.target.result
console.log('成功打开' + dbname)
}
// 打开成功后,如果版本有变化自动执行以下事件
request.onupgradeneeded = function(e) {
var db = e.target.result
if (!db.objectStoreNames.contains(storeName)) {
// 如果表格不存在,创建一个新的表格(keyPath,主键 ; autoIncrement,是否自增),会返回一个对象(objectStore)
var objectStore = db.createObjectStore(storeName, {
keyPath: 'id',
autoIncrement: true
})
// 指定可以被索引的字段,unique字段是否唯一, 指定索引可以加快搜索效率。
objectStore.createIndex(key, key, {
unique: true
})
}
console.log('数据库版本更改为: ' + dbversion)
}
}
indexedDB.open()方法返回一个 IDBRequest 对象。这个对象通过三种事件error、success、upgradeneeded,处理打开数据库的操作结果。
3、设置数据 setData
/**
* [setData 设置数据]
* @param {[Object]} opt [要储存的数据]
*/
OpenIndexDB.prototype.setData = function(opt) {
// 定义indexDB的各项信息
const $this = this
const db = this.dbInfo.db
const storeName = this.dbInfo.store
const key = this.dbInfo.key
// 创建一个事务
const transaction = db.transaction(storeName, 'readwrite')
// 通过事务来获取store
const store = transaction.objectStore(storeName)
// 构建数据对象
const obj = {}
obj[key] = opt.key
obj['value'] = opt.value
// 往数据库添加数据
const addData = store.add(obj)
addData.onsuccess = function(e) {
console.log('数据添加成功')
}
addData.onerror = function(e) {
// 数据已存在就更新数据
if (e.target.error.code === 0) {
$this.updateData(opt.key, opt.value)
}
}
}
4、获取数据
/**
* [getData 获取数据]
* @param {[String]} keyname [通过key值名称获取数据]
* @param {[Function]} callback [回调函数]
*/
OpenIndexDB.prototype.getData = function(keyname, callback) {
if (!keyname) { return }
// 定义indexDB的各项信息
const db = this.dbInfo.db
const storeName = this.dbInfo.store
const key = this.dbInfo.key
// 创建一个事务
const transaction = db.transaction(storeName, 'readwrite')
// 通过事务来获取store
const store = transaction.objectStore(storeName)
// 根据索引获取数据
const index = store.index(key)
// 根据指定的keyname获取数据
const request = index.get(keyname)
// 成功获取数据时调用
request.onsuccess = function(e) {
const data = e.target.result
// 调用回调函数
callback(data)
}
request.onerror = function(e) {
console.log(e)
}
}
5、更新数据
/**
* [updateData 更新数据]
* @param {[String]} keyname [通过key值名称获取数据]
* @param {[Any]} updateData [原本的数据、替换的数据]
* @param {[String]} addKey [添加的值,有该值代码在原值后面增加,否则updateData 全部替换初始值]
*/
OpenIndexDB.prototype.updateData = function(keyname, updateData, addKey) {
if (!keyname) { return }
// 定义indexDB的各项信息
const db = this.dbInfo.db
const storeName = this.dbInfo.store
const key = this.dbInfo.key
// 创建一个事务
const transaction = db.transaction(storeName, 'readwrite')
// 通过事务来获取store
const store = transaction.objectStore(storeName)
// 根据索引获取数据
const index = store.index(key)
// 根据指定的keyname获取数据
const request = index.get(keyname)
// 成功获取数据时调用
request.onsuccess = function(e) {
const data = e.target.result
// 改变数据
if (addKey) {
data['value'][addKey] = updateData
} else {
data['value'] = updateData
}
// 更新数据
store.put(data)
}
request.onerror = function(e) {
console.log(e)
}
}
6、删除数据库
/**
* [deleteDatabase 删除数据库]
* @param {[String]} bdName [数据库名称]
*/
OpenIndexDB.prototype.deleteDatabase = function() {
const bdName = this.dbInfo.name
if (!bdName) { return }
var DBDeleteRequest = window.indexedDB.deleteDatabase(bdName)
DBDeleteRequest.onerror = function(event) {
console.log('Error')
}
DBDeleteRequest.onsuccess = function(event) {
console.log('success')
}
}
7、调用例子
/**
* VUE项目为例
* 在mian.js中引入IndexDB文件
* 创建数据库
*/
Vue.prototype.$indexDB = new IndexDB({
dbname: 'Test', // 数据库名称
dbversion: 1, // 版本号
store: 'billStore' // 表名称
})
/**
* VUE项目为例
* 设置数据
*/
this.$indexDB.setData({
key: 'dictionary',
value: {d: 12}
})
/**
* VUE项目为例
* 获取数据数据
*/
// 引入GETIndexDataCallback
import GETIndexDataCallback from '@/utils/get-indexedDB-callback'
import { getDictionaryList } from '@/api/common'
const opt = {
dbName: this.$indexDB, // 数据库名称
key: 'Dictionary',
api: getDictionaryList // api接口,找不到数据后,请求的接口
}
GETIndexDataCallback(opt, (data) => {
console.log(data)
})
/**
* VUE项目为例
* 封装获取数据,因为很多获取的时候获取可能是空的,所以需要调接口,所以综合一个方法请求
*/
function GETIndexDataCallback(opt, callback) {
if (!opt.dbName) {
return
}
// 未setData直接getData,会transaction找不到
try {
opt.dbName.getData(opt.key, function(data) {
// sessionStorage.getItem('setIndexedDB')是用于每次进来都更新的依据,这里可以接口自定义
if (data && sessionStorage.getItem('setIndexedDB')) {
callback && callback(data.value)
} else {
request()
}
})
} catch {
request()
}
// 调接口
async function request() {
try {
const res = await opt.api()
if (res && res.status === 0) {
let typeObj = res.data
opt.dbName.setData({
key: opt.key,
value: typeObj
})
sessionStorage.setItem('setIndexedDB', 1)
callback && callback(typeObj)
}
} catch {
console.log('res')
}
}
}
export default GETIndexDataCallback