简介
IndexedDB是HTML5规范里新出现的浏览器里内置的数据库。对于在浏览器里存储数据,你可以使用cookies或local storage,但它们都是比较简单的技术,而IndexedDB提供了类似数据库风格的数据存储和使用方式。存储在IndexedDB里的数据是永久保存,不像cookies那样只是临时的。IndexedDB里提供了查询数据的功能,在online和offline模式下都能使用。你可以用IndexedDB存储大型数据。
IndexedDB里数据以对象的形式存储,每个对象都有一个key值索引。IndexedDB里的操作都是事务性的。一种对象存储在一个objectStore里,objectStore就相当于关系数据库里的表。IndexedDB可以有很多objectStore,objectStore里可以有很多对象。每个对象可以用key值获取。
IndexedDB vs LocalStorage
IndexedDB和LocalStorage都是用来在浏览器里存储数据,但它们使用不同的技术,有不同的用途,你需要根据自己的情况适当的选择使用哪种。LocalStorage是用key-value键值模式存储数据,但跟IndexedDB不一样的是,它的数据并不是按对象形式存储。它存储的数据都是字符串形式。如果你想让LocalStorage存储对象,你需要借助JSON.stringify()能将对象变成字符串形式,再用JSON.parse()将字符串还原成对象。但如果要存储大量的复杂的数据,这并不是一种很好的方案。毕竟,localstorage就是专门为小数量数据设计的,它的api是同步的。
IndexedDB很适合存储大量数据,它的API是异步调用的。IndexedDB使用索引存储数据,各种数据库操作放在事务中执行。IndexedDB甚至还支持简单的数据类型。IndexedDB比localstorage强大得多,但它的API也相对复杂。
对于简单的数据,你应该继续使用localstorage,但当你希望存储大量数据时,IndexedDB会明显的更适合,IndexedDB能提供你更为复杂的查询数据的方式。
IndexedDB vs Web SQL
WebSQL也是一种在浏览器里存储数据的技术,跟IndexedDB不同的是,IndexedDB更像是一个NoSQL数据库,而WebSQL更像是关系型数据库,使用SQL查询数据。W3C已经不再支持这种技术。
具体情况请看:www.w3.org/TR/webdatab…
因为不再支持,所以你就不要在项目中使用这种技术了。
IndexedDB vs Cookies
每次HTTP接受和发送都会传递Cookies数据,它会占用额外的流量。例如,如果你有一个10KB的Cookies数据,发送10次请求,那么,总计就会有100KB的数据在网络上传输。Cookies只能是字符串。浏览器里存储Cookies的空间有限,很多用户禁止浏览器使用Cookies。所以,Cookies只能用来存储小量的非关键的数据。
IndexedDB的浏览器支持
不同的浏览器对于IndexedDB有不同的实现,正常来说,我们可以使用window.indexedDB来获取到浏览器的indexedDB对象。但是对于某些浏览器来说,还没有使用标准的window.indexedDB,而是用带前缀的实现。
所以我们在使用过程中通常需要进行判断和转换:
// In the following line, you should include the prefixes of implementations you want to test.
window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
// DON'T use "var indexedDB = ..." if you're not in a function.
// Moreover, you may need references to some window.IDB* objects:
window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction || {READ_WRITE: "readwrite"}; // This line should only be needed if it is needed to support the object's constants for older browsers
window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
// (Mozilla has never prefixed these objects, so we don't need window.mozIDB*)
复制代码
上面我们从window获取了indexedDB,IDBTransaction和IDBKeyRange三个对象。
其中indexedDB表示的是数据库的连接。IDBTransaction表示的是transaction,而IDBKeyRange则是用从数据库的某个特定key range中取出数据。
但是,通常来说带前缀的实现一般都是不稳定的,所以我们通常不建议在正式环境中使用,所以如果不支持标准表达式的话,需要直接报错:
if (!window.indexedDB) {
console.log("Your browser doesn't support a stable version of IndexedDB. Such and such feature will not be available.");
}
复制代码
阮一峰 : 浏览器数据库 IndexedDB 入门教程
使用场景
结合它的优、缺点谈谈其使用场景。
- 不适合过大的数据存储,浏览器对 indexDB 有 50M 大小的限制
- 不适合对兼容性要求高的项目
- 不适合存储敏感数据
- 当用户清除浏览器缓存的时候可能出现问题
- indexedDB 受到同源策略的限制
工具方法
/**
* 封装的方法以及用法
<!-- <script type="module">
import { openDB,
addData,
getDataByKey,
cursorGetData,
getDataByIndex,
cursorGetDataByIndex,
updateDB,
deleteDB,
cursorDelete,
closeDB,
deleteDBAll } from './indexedDb.js'
openDB('myDB', 'one').then(db => { // 打开 / 创建 Indexed 数据
// addData(db, 'one', { id: 1, name: '张三', age: 24 }) // 新增数据
// addData(db, 'one', { id: 2, name: '李四', age: 30 }) // 新增数据
// getDataByKey(db, 'one', 1) // 通过主键读取数据
// cursorGetData(db, 'one') // 通过游标读取数据
// getDataByIndex(db, 'one','name', '张三') // 通过索引读取数据
// cursorGetDataByIndex(db, 'one', 'name', '张三') // 通过索引和游标查询记录
// updateDB(db, 'one', { id: 1, name: '张三', age: 25 }) // 更新数据
// deleteDB(db, 'one', 2) // 删除数据
// cursorDelete(db, 'one', 'name', '张三') // 通过索引和游标删除指定的数据
// closeDB(db) // 关闭数据库
})
// deleteDBAll('myDB') // 删除数据库 慎用!!!!!!!!!!
</script> -->
*
* 打开数据库
* @param {object} dbName 数据库的名字
* @param {string} storeName 仓库名称
* @param {string} version 数据库的版本
* @return {object} 该函数会返回一个数据库实例
*/
export function openDB(dbName, storeName, version = 1) {
return new Promise((resolve, reject) => {
// 兼容浏览器
var indexedDB =
window.indexedDB ||
window.mozIndexedDB ||
window.webkitIndexedDB ||
window.msIndexedDB
let db
const request = indexedDB.open(dbName, version)
request.onsuccess = function (event) {
db = event.target.result // 数据库对象
console.log('数据库打开成功')
resolve(db)
}
request.onerror = function (event) {
console.log('数据库打开报错')
}
request.onupgradeneeded = function (event) {
// 数据库创建或升级的时候会触发
console.log('onupgradeneeded')
db = event.target.result // 数据库对象
var objectStore
if (!db.objectStoreNames.contains(storeName)) {
objectStore = db.createObjectStore(storeName, { keyPath: 'id' }) // 创建表
objectStore.createIndex('name', 'name', { unique: false }) // 创建索引 可以让你搜索任意字段
// objectStore.createIndex('address', 'address', { unique: false })
// objectStore.createIndex('nameAddr', ['name', 'address'], {
// unique: false,
// })
// objectStore.createIndex('flag', 'flag', { unique: false })
}
}
})
}
/**
* 新增数据
* @param {object} db 数据库实例
* @param {string} storeName 仓库名称
* @param {string} data 数据
*/
export function addData(db, storeName, data) {
var request = db
.transaction([storeName], 'readwrite') // 事务对象 指定表格名称和操作模式("只读"或"读写")
.objectStore(storeName) // 仓库对象
.add(data)
request.onsuccess = function (event) {
console.log('数据写入成功')
}
request.onerror = function (event) {
console.log('数据写入失败')
throw new Error(event.target.error)
}
}
/**
* 通过主键读取数据
* @param {object} db 数据库实例
* @param {string} storeName 仓库名称
* @param {string} key 主键值
*/
export function getDataByKey(db, storeName, key) {
return new Promise((resolve, reject) => {
var transaction = db.transaction([storeName]) // 事务
var objectStore = transaction.objectStore(storeName) // 仓库对象
var request = objectStore.get(key)
request.onerror = function (event) {
console.log('事务失败')
}
request.onsuccess = function (event) {
console.log('主键查询结果: ', request.result)
resolve(request.result)
}
})
}
/**
* 通过游标读取数据
* @param {object} db 数据库实例
* @param {string} storeName 仓库名称
*/
export function cursorGetData(db, storeName) {
let list = []
var store = db
.transaction(storeName, 'readwrite') // 事务
.objectStore(storeName) // 仓库对象
var request = store.openCursor() // 指针对象
request.onsuccess = function (e) {
var cursor = e.target.result
if (cursor) {
// 必须要检查
list.push(cursor.value)
cursor.continue() // 遍历了存储对象中的所有内容
} else {
console.log('游标查询结果:', list)
}
}
}
/**
* 通过索引读取数据
* @param {object} db 数据库实例
* @param {string} storeName 仓库名称
* @param {string} indexName 索引名称
* @param {string} indexValue 索引值
*/
export function getDataByIndex(db, storeName, indexName, indexValue) {
var store = db.transaction(storeName, 'readwrite').objectStore(storeName)
var request = store.index(indexName).get(indexValue)
request.onerror = function () {
console.log('事务失败')
}
request.onsuccess = function (e) {
var result = e.target.result
console.log('索引查询结果:', result)
}
}
/**
* 通过索引和游标查询记录
* @param {object} db 数据库实例
* @param {string} storeName 仓库名称
* @param {string} indexName 索引名称
* @param {string} indexValue 索引值
*/
export function cursorGetDataByIndex(db, storeName, indexName, indexValue) {
let list = []
var store = db.transaction(storeName, 'readwrite').objectStore(storeName) // 仓库对象
var request = store
.index(indexName) // 索引对象
.openCursor(IDBKeyRange.only(indexValue)) // 指针对象
request.onsuccess = function (e) {
var cursor = e.target.result
if (cursor) {
// 必须要检查
list.push(cursor.value)
cursor.continue() // 遍历了存储对象中的所有内容
} else {
console.log('游标索引查询结果:', list)
}
}
request.onerror = function (e) {}
}
/**
* 更新数据
* @param {object} db 数据库实例
* @param {string} storeName 仓库名称
* @param {object} data 数据
*/
export function updateDB(db, storeName, data) {
var request = db
.transaction([storeName], 'readwrite') // 事务对象
.objectStore(storeName) // 仓库对象
.put(data)
request.onsuccess = function () {
console.log('数据更新成功')
}
request.onerror = function () {
console.log('数据更新失败')
}
}
/**
* 删除数据
* @param {object} db 数据库实例
* @param {string} storeName 仓库名称
* @param {object} id 主键值
*/
export function deleteDB(db, storeName, id) {
var request = db
.transaction([storeName], 'readwrite')
.objectStore(storeName)
.delete(id)
request.onsuccess = function () {
console.log('数据删除成功')
}
request.onerror = function () {
console.log('数据删除失败')
}
}
/**
* 通过索引和游标删除指定的数据
* @param {object} db 数据库实例
* @param {string} storeName 仓库名称
* @param {string} indexName 索引名
* @param {object} indexValue 索引值
*/
export function cursorDelete(db, storeName, indexName, indexValue) {
var store = db.transaction(storeName, 'readwrite').objectStore(storeName)
var request = store
.index(indexName) // 索引对象
.openCursor(IDBKeyRange.only(indexValue)) // 指针对象
request.onsuccess = function (e) {
var cursor = e.target.result
var deleteRequest
if (cursor) {
deleteRequest = cursor.delete() // 请求删除当前项
deleteRequest.onerror = function () {
console.log('游标删除该记录失败')
}
deleteRequest.onsuccess = function () {
console.log('游标删除该记录成功')
}
cursor.continue()
}
}
request.onerror = function (e) {}
}
/**
* 关闭数据库
* @param {object} db 数据库实例
*/
export function closeDB(db) {
db.close()
console.log('数据库已关闭')
}
/**
* 删除数据库
* @param {object} dbName 数据库名称
*/
export function deleteDBAll(dbName) {
console.log(dbName)
let deleteRequest = window.indexedDB.deleteDatabase(dbName)
deleteRequest.onerror = function (event) {
console.log('删除失败')
}
deleteRequest.onsuccess = function (event) {
console.log('删除成功')
}
}
复制代码