在项目开发过程中,前端需要存储大量的数据。cookie, localstorage 都有存储长度限制。首先总结一下各个存储的参数
IndexedDB 属于非关系型数据库。(不支持SQL查询)
特点:
键值对储存 IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以"键值对"的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。
异步 IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。
支持事务 IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。
同源限制 IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。
支持二进制储存 IndexedDB 不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象。
储存空间大 IndexedDB 的储存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有上限。储 存 在 电 脑 上 中 的 位 置 为 C:\Users\当 前 的 登 录 用 …\Default\IndexedDB
核心概念
数据库:IDBDatabase 对象,数据库有版本概念,同一时刻只能有一个版本,每个域名可以建多个数据库
对象仓库:IDBObjectStore 对象,类似于关系型数据库的表格
索引: IDBIndex 对象,可以在对象仓库中,为不同的属性建立索引,主键建立默认索引
事务: IDBTransaction 对象,增删改查都需要通过事务来完成,事务对象提供了error,abord,complete三个回调方法,监听操作结果
操作请求:IDBRequest 对象
指针: IDBCursor 对象
主键集合:IDBKeyRange 对象,主键是默认建立索引的属性,可以取当前层级的某个属性,也可以指定下一层对象的属性,还可以是一个递增的整数
/**
* 打开或者创建数据库连接
* @param {*} dbName 数据库名称
* @param {*} verson 数据库版本
*/
function openDB(dbName, verson = 1, tableName, uuid) {
return new Promise((resolve, reject) => {
const indexdDB = window.indexedDB
let db
// 打开数据库,如果没有就创建
const request = indexdDB.open(dbName, verson)
// 数据库打开成功的回调
request.onsuccess = function (event) {
db = event.target.result // 数据库对象
console.log('数据库打开成功了')
resolve(db)
}
// 数据库打开失败的回调
request.onerror = function (event) {
console.log('数据库打开失败')
reject(event)
}
// 数据库有更新时的回调 数据库创建或者升级的时候会被触发
request.onupgradeneeded = function (event) {
db = event.target.result
let objStorage = {}
objStorage = db.createObjectStore(
tableName, // 创建表名称为users
{
keyPath: uuid, // 主键
autoIncrement: true // 是否实现自增
}
)
// 创建索引,在后面查询数据的时候可以根据索引查询
objStorage.createIndex(uuid, uuid, { unique: true }) // 因为uuid是主键,所以不能为空不可重复
objStorage.createIndex('name', 'name', { unique: false })
objStorage.createIndex('age', 'age', { unique: false })
}
})
}
/**
* 插入数据
* @param {*} db 数据库对象
* @param {*} storeName 仓库名
* @param {*} data 插入的数据
*/
function addData(db, storeName, data) {
return new Promise((resolve, reject) => {
const request = db
.transaction([storeName], 'readwrite') // 事物对象 指定表格名称和操作模式 只读 或修改
.objectStore(storeName) // 创建仓库对象
.add(data)
console.log(request, 'request')
request.onsuccess = function (event) {
console.log('数据写入成功')
resolve(event)
}
request.onerror = function (event) {
console.log('数据写入失败')
reject(event)
}
})
}
/**
* 通过主键查询数据
* @param {*} db
* @param {*} storeName
* @param {*} key 主键名
* @returns
*/
function getDtaByKey(db, storeName, key) {
return new Promise((resolve, reject) => {
const transaction = db.transaction([storeName])
const objectStore = transaction.objectStore(storeName)
// const request = objectStore.getAll() // 也是查询表里面的所有数据
const request = objectStore.get(key)
request.onsuccess = function (event) {
console.log('查找成功')
resolve(event)
}
request.onerror = function (event) {
console.log('查找失败')
reject(event)
}
})
}
/**
* 开启游标查询
*/
function cursorGetData(db, storeName) {
return new Promise((resolve, reject) => {
const list = []
const store = db.transaction(storeName, 'readwrite').objectStore(storeName)
const request = store.openCursor() // 指针对象
// 游标开启成功
request.onsuccess = function (e) {
const cursor = e.target.result
// 注意这个地方需要做判断,当我们没有数据的时候,那么这个值是空的,他会一直执行
if (cursor) {
list.push(cursor.value)
cursor.continue() //继续遍历存储对象中的所有内容
} else {
console.log('游标对去的数据', list)
}
}
// 游标开启失败
request.onerror = function (e) {
console.log('游标开启失败了')
reject(e)
}
})
}
/**
* 通过索引查询数据
* @param {*} db
* @param {*} storeName
* @param {*} indexName 索引名称
* @param {*} indexValue 索引值
* @returns
*/
function getDataByIndex(db, storeName, indexName, indexValue) {
return new Promise((resolve, reject) => {
const store = db.transaction(storeName, 'readwrite').objectStore(storeName)
const request = store.index(indexName).get(indexValue)
request.onsuccess = function (e) {
const result = e.target.result
resolve(result)
}
request.onerror = function (err) {
console.log(err)
reject(err)
}
})
}
/**
* 通过游标和索引查询数据
* @param {*} db
* @param {*} storeName
* @param {*} indexName
* @param {*} indexValue
* @returns
*/
function cursorGetByIndex(db, storeName, indexName, indexValue) {
return new Promise((resolve, reject) => {
let list = []
const store = db.transaction(storeName, 'readwrite').objectStore(storeName)
// store.index(indexName) 索引对象 IDBKeyRange.only(indexValue) 只针对想
const request = store.index(indexName).openCursor(IDBKeyRange.only(indexValue))
request.onsuccess = function (e) {
const cursor = e.target.result
if (cursor) {
list.push(cursor.value)
cursor.continue()
} else {
console.log('游标索引查询结果', list)
resolve(list)
}
}
request.onerror = function (e) {
console.log('查询失败')
reject(e)
}
})
}
function getByIndexPage(db, storeName, indexName, indexValue, page, pageSize) {
return new Promise((resolve, reject) => {
let list = []
let counter = 0 // 计数器
let advanced = true // 是否跳过多少条查询, 如果是false 查询全部数据
const store = db.transaction(storeName, 'readwrite').objectStore(storeName)
const request = store.index(indexName).openCursor(IDBKeyRange.only(indexValue))
request.onsuccess = function (e) {
const cursor = e.target.result
// 查询大于第一页的数据,从第二页开始
if (page > 1 && advanced) {
advanced = false
list.push(cursor.value)
cursor.advance((page - 1) * pageSize) // 跳过多少条
return
}
if (cursor) {
list.push(cursor.value)
counter++
if (counter < pageSize) {
cursor.continue()
} else {
cursor = null
console.log('分野查询结果', list)
}
} else {
console.log(list, '分页结果')
}
}
request.onerror = function (e) {
console.log('查询失败')
reject(e)
}
})
}
/**
* 更新数据
*/
function upDateDB(db, storeName, data) {
return new Promise((resolve, reject) => {
const store = db.transaction(storeName, 'readwrite').objectStore(storeName).put(data)
request.onsuccess = function (e) {
const result = e.target.result
resolve(result)
}
request.onerror = function (err) {
console.log(err)
reject(err)
}
})
}
/**
* 通过主键删除数据
*/
function deleteDB(db, storeName, id) {
return new Promise((resolve, reject) => {
const store = db.transaction(storeName, 'readwrite').objectStore(storeName).delete(id)
request.onsuccess = function (e) {
const result = e.target.result
resolve(result)
}
request.onerror = function (err) {
console.log(err)
reject(err)
}
})
}
/**
* 通过索引和游标删除数据
*/
function cursorDelete(db, storeName, indexName, indexValue) {
const store = db.transaction(storeName, 'readwrite').objectStore(storeName)
const request = store.index(indexName).openCursor(IDBKeyRange.only(indexValue))
request.onsuccess = function (e) {
let cursor = e.target.result
let deleteRequest
if (cursor) {
deleteRequest = cursor.delete() // 请求删除当前项
deleteRequest.onerror = function () {
console.log('删除失败')
}
deleteRequest.onsuccess = function () {
console.log('删除成功')
}
}
}
}
/**
* 关闭数据库
*/
function closeDB(db) {
db.close()
}
/**
* 删除数据库
*/
function deleteDBALL(dbName) {
return new Promise((resolve, reject) => {
let deleteRquest = window.indexedDB.deleteDatabase(dbName)
deleteRquest.onerror = function () {
console.log('删除失败')
resolve(succ)
}
deleteRquest.onsuccess = function (err) {
console.log('删除成功')
reject(err)
}
})
}
/**
* 生成唯一id的方法
* @returns
*/
function generateUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = (Math.random() * 16) | 0,
v = c === 'x' ? r : (r & 0x3) | 0x8
return v.toString(16)
})
}
export {
openDB,
addData,
generateUUID,
getDtaByKey,
cursorGetData,
getDataByIndex,
cursorGetByIndex,
getByIndexPage,
closeDB,
deleteDBALL
}
// 添加
async handleClickA() {
const db = await openDB('class', 1, 'users', 'uuid')
let data = {
uuid: generateUUID(), // 插入uuid时,不能为空否则报错
name: '你个老六',
age: Math.round(Math.random() * 30),
class: [1, 2, 3, 4, 5, 6],
sex: [{ a: 1, b: 2, c: 3 }]
}
await addData(db, 'users', data)
},
// 通过主键搜索 某个表里面的某个数据
async handleClickAsearch() {
let list = {}
const db = await openDB('class', 1)
const res = await getDtaByKey(db, 'users', 'd6f161b7-fe0e-43e3-8e3e-d9a0ddf3951c')
if (res.type === 'success') {
list = res.target.result
}
console.log(list)
},
// 通过游标查询 可以理解为查某个表
async handleClickAcours() {
const db = await openDB('class', 1)
const res = await cursorGetData(db, 'users')
console.log(res)
},
// 通过索引查询
async handleClickAvalues() {
const db = await openDB('class', 1)
const res = await getDataByIndex(db, 'users', 'age', 19)
console.log(res)
},
//通过游标和索引查询数据
async handleClickAandKey() {
const db = await openDB('class', 1)
const res = await cursorGetByIndex(db, 'users', 'age', 19)
console.log(res)
},
// 通过游标和索引查询数据分页
async handleClickAandKeyPage() {
const db = await openDB('class', 1)
const res = await getByIndexPage(db, 'users', 'age', 29, 1, 10)
console.log(res)
}