IndexedDB 是一种底层 API,用于在客户端存储大量的结构化数据(也包括文件/二进制大型对象(blobs))。该 API 使用索引实现对数据的高性能搜索
浏览器存储对比
特性 | cookie | sessionStorage | localStorage | indexedDB |
---|---|---|---|---|
数据存储周期 | 一般由服务器生成,可以设置过期时间; 前端采用和js-cookie等组件也可以生成 | 页面关闭就清理刷新依然存在 | 除非被清理,否则一直存在; 浏览器关闭还会保存在本地 | 除非被清理,否则一直存在 |
是否支持跨页面 | 是 | 否 | 是 | 是 |
是否支持跨浏览器 | 否 | 否 | 否 | 是 |
数据存储大小 | 4K | SessionStorage默认能够存储5-10MB的数据根据当前浏览器的空闲空间来判断能够分配多少存储空间 | Safari: 5MB ;Chrome: 10MB ;Internet Explorer: 10MB;Mozilla Firefox: 10MB ;Opera: 10MB | 不限制大小 |
与服务端通信 | 每次都会携带在请求的header 中,对于请求性能有影响; 同时由于请求中都带有,所以也容易出现安全问题 | 不参与 | 不参与 | 不参与 |
特点 | 字符串键值对在本地存储数据 | 字符串键值对在本地存储数据 | 字符串键值对在本地存储数据 | IndexedDB 是一个非关系型数据库(不支持通过 SQL 语句操作),用于在客户端存储大量的结构化数据(也包括文件/二进制大型对象(blobs))。该 API 使用索引实现对数据的高性能搜索 |
通过以上对比,我们如果需要使用到大数据存储且跨越浏览器的时候可以使用indexedDB进行处理,我们先看下浏览器的支持情况
浏览器支持情况
使用场景
IndexDB 是一种浏览器内置的数据库,它可以在浏览器中存储大量的数据,而不需要依赖于服务器。
- 离线应用
IndexDB 可以在浏览器中存储大量的数据,这使得它非常适合用于离线应用。 当用户离线时,应用程序可以从IndexDB 中读取数据, 而不需要依赖于服务器。这使得应用程序可以在没有网络连接的情况下继续运行,从而提高了用户体验。
- 缓存数据
IndexDB 可以用于缓存数据,这使得应用程序可以更快地加载数据。
当应用程序需要加载数据时,它可以首先从IndexDB中读取数据,
而不需要从服务器中获取数据。这可以大大减少网络请求的数量,
从而提高应用程序的性能。
- 数据存储
IndexDB 可以用于存储大量的数据,这使得它非常适合用于需要存
储大量数据的应用程序。例如,一个在线笔记应用程序可以使用
IndexDB 来存储用户的笔记,而不需要依赖于服务器。
- 数据同步
IndexDB 可以用于数据同步,这使得应用程序可以在多个设备之间
同步数据。当用户在一个设备上更新数据时,应用程序可以将数据
存储在 IndexDB 中,并将数据同步到其他设备上。这可以使用户在
不同设备上访问相同的数据,从而提高用户体验。
浏览器查看位置
- 左侧为Chrome ,右侧为Safari
数据库操作
新增打开数据库
js
/**
* version:如果需要新增表,需要加版本号
* 打开数据库
*/
export function openDB(dbName, storeName, version = 1) {
return new Promise((resolve, reject) => {
let indexedDB = window.indexedDB
let db
const request = indexedDB.open(dbName, version)
request.onsuccess = function (event) {
db = event.target.result // 数据库对象
resolve(db)
}
request.onerror = function (event) {
reject(event)
}
request.onupgradeneeded = function (event) {
// 数据库创建或升级的时候会触发
console.log('onupgradeneeded')
db = event.target.result // 数据库对象
let objectStore
if (!db.objectStoreNames.contains(storeName)) {
objectStore = db.createObjectStore(storeName, { keyPath: 'id' }) // 创建表
// objectStore.createIndex('name', 'name', { unique: true }) // 创建索引 可以让你搜索任意字段
}
}
})
}
删除数据库
js
/**
* 删除数据库
*/
export function deleteDBAll(dbName) {
console.log(dbName)
let deleteRequest = window.indexedDB.deleteDatabase(dbName)
return new Promise((resolve, reject) => {
deleteRequest.onerror = function (event) {
console.log('删除失败')
}
deleteRequest.onsuccess = function (event) {
console.log('删除成功')
}
})
}
关闭数据库
js
/**
* 关闭数据库
*/
export function closeDB(db) {
db.close()
console.log('数据库已关闭')
}
数据处理
新增数据
js
/**
* 新增数据
*/
export function addData(db, storeName, data) {
return new Promise((resolve, reject) => {
let request = db.transaction([storeName], 'readwrite') // 事务对象 指定表格名称和操作模式("只读"或"读写")
.objectStore(storeName) // 仓库对象
.add(data)
request.onsuccess = function (event) {
resolve(event)
}
request.onerror = function (event) {
throw new Error(event.target.error)
reject(event)
}
})
}
通过主键读取数据
js
/**
* 通过主键读取数据
*/
export function getDataByKey(db, storeName, key) {
return new Promise((resolve, reject) => {
let transaction = db.transaction([storeName]) // 事务
let objectStore = transaction.objectStore(storeName) // 仓库对象
let request = objectStore.get(key)
request.onerror = function (event) {
reject(event)
}
request.onsuccess = function (event) {
resolve(request.result)
}
})
}
更新数据
js
export function updateDB(db, storeName, data) {
let request = db.transaction([storeName], 'readwrite') // 事务对象
.objectStore(storeName) // 仓库对象
.put(data)
return new Promise((resolve, reject) => {
request.onsuccess = function (ev) {
resolve(ev)
}
request.onerror = function (ev) {
resolve(ev)
}
})
}
删除数据
js
export function deleteDB(db, storeName, id) {
let request = db.transaction([storeName], 'readwrite').objectStore(storeName).delete(id)
return new Promise((resolve, reject) => {
request.onsuccess = function (ev) {
resolve(ev)
}
request.onerror = function (ev) {
resolve(ev)
}
})
}
获取全部数据
js
/**
* 获取所有数据
*/
export function getDataAll(db, storeName) {
let store = db.transaction(storeName, 'readwrite').objectStore(storeName)
var request = store.getAll();
return new Promise((resolve, reject) => {
request.onerror = function (e) {
reject(e)
}
request.onsuccess = function (e) {
resolve(request.result)
}
})
}
第三方组件库
完整代码
IndexDB.js
js
/**
* 封装的方法以及用法
* 打开数据库
*/
export function openDB(dbName, storeName, version = 1) {
return new Promise((resolve, reject) => {
let indexedDB = window.indexedDB
let db
const request = indexedDB.open(dbName, version)
request.onsuccess = function (event) {
db = event.target.result // 数据库对象
resolve(db)
}
request.onerror = function (event) {
reject(event)
}
request.onupgradeneeded = function (event) {
// 数据库创建或升级的时候会触发
console.log('onupgradeneeded')
db = event.target.result // 数据库对象
let objectStore
if (!db.objectStoreNames.contains(storeName)) {
objectStore = db.createObjectStore(storeName, { keyPath: 'id' }) // 创建表
// objectStore.createIndex('name', 'name', { unique: true }) // 创建索引 可以让你搜索任意字段
}
}
})
}
/**
* 新增数据
*/
export function addData(db, storeName, data) {
return new Promise((resolve, reject) => {
let request = db.transaction([storeName], 'readwrite') // 事务对象 指定表格名称和操作模式("只读"或"读写")
.objectStore(storeName) // 仓库对象
.add(data)
request.onsuccess = function (event) {
resolve(event)
}
request.onerror = function (event) {
throw new Error(event.target.error)
reject(event)
}
})
}
/**
* 通过主键读取数据
*/
export function getDataByKey(db, storeName, key) {
return new Promise((resolve, reject) => {
let transaction = db.transaction([storeName]) // 事务
let objectStore = transaction.objectStore(storeName) // 仓库对象
let request = objectStore.get(key)
request.onerror = function (event) {
reject(event)
}
request.onsuccess = function (event) {
resolve(request.result)
}
})
}
/**
* 通过游标读取数据
*/
export function cursorGetData(db, storeName) {
let list = []
let store = db.transaction(storeName, 'readwrite') // 事务
.objectStore(storeName) // 仓库对象
let request = store.openCursor() // 指针对象
return new Promise((resolve, reject) => {
request.onsuccess = function (e) {
let cursor = e.target.result
if (cursor) {
// 必须要检查
list.push(cursor.value)
cursor.continue() // 遍历了存储对象中的所有内容
} else {
resolve(list)
}
}
request.onerror = function (e) {
reject(e)
}
})
}
/**
* 通过索引读取数据
*/
export function getDataByIndex(db, storeName, indexName, indexValue) {
let store = db.transaction(storeName, 'readwrite').objectStore(storeName)
let request = store.index(indexName).get(indexValue)
return new Promise((resolve, reject) => {
request.onerror = function (e) {
reject(e)
}
request.onsuccess = function (e) {
resolve(e.target.result)
}
})
}
/**
* 获取所有数据
*/
export function getDataAll(db, storeName) {
let store = db.transaction(storeName, 'readwrite').objectStore(storeName)
var request = store.getAll();
return new Promise((resolve, reject) => {
request.onerror = function (e) {
reject(e)
}
request.onsuccess = function (e) {
resolve(request.result)
}
})
}
/**
* 通过索引和游标查询记录
*/
export function cursorGetDataByIndex(db, storeName, indexName, indexValue) {
let list = []
let store = db.transaction(storeName, 'readwrite').objectStore(storeName) // 仓库对象
let request = store.index(indexName) // 索引对象
.openCursor(IDBKeyRange.only(indexValue)) // 指针对象
return new Promise((resolve, reject) => {
request.onsuccess = function (e) {
let cursor = e.target.result
if (cursor) {
list.push(cursor.value)
cursor.continue() // 遍历了存储对象中的所有内容
} else {
resolve(list)
}
}
request.onerror = function (ev) {
reject(ev)
}
})
}
/**
* 更新数据
*/
export function updateDB(db, storeName, data) {
let request = db.transaction([storeName], 'readwrite') // 事务对象
.objectStore(storeName) // 仓库对象
.put(data)
return new Promise((resolve, reject) => {
request.onsuccess = function (ev) {
resolve(ev)
}
request.onerror = function (ev) {
resolve(ev)
}
})
}
/**
* 删除数据
*/
export function deleteDB(db, storeName, id) {
let request = db.transaction([storeName], 'readwrite').objectStore(storeName).delete(id)
return new Promise((resolve, reject) => {
request.onsuccess = function (ev) {
resolve(ev)
}
request.onerror = function (ev) {
resolve(ev)
}
})
}
/**
* 删除数据库
*/
export function deleteDBAll(dbName) {
console.log(dbName)
let deleteRequest = window.indexedDB.deleteDatabase(dbName)
return new Promise((resolve, reject) => {
deleteRequest.onerror = function (event) {
console.log('删除失败')
}
deleteRequest.onsuccess = function (event) {
console.log('删除成功')
}
})
}
/**
* 关闭数据库
*/
export function closeDB(db) {
db.close()
console.log('数据库已关闭')
}
export default {
openDB,
addData,
getDataByKey,
cursorGetData,
getDataByIndex,
cursorGetDataByIndex,
updateDB,
deleteDB,
deleteDBAll,
closeDB,
getDataAll
}
app.tsx
tsx
import './app.css'
import IndexDB from './IndexDB.js'
import { Card, Button, Table, Row, Col, Space, Modal } from 'antd';
import { useState, useEffect } from 'react';
import ModalForm from './ModalForm';
export function App() {
const [dataSource, setDataSource] = useState([])
const dbName = 'myDB', storeName = 'db_1'
async function createDB() {
await IndexDB.openDB(dbName, storeName, 1)
}
async function getAll() {
const db = await IndexDB.openDB(dbName, storeName, 1)
var data = await IndexDB.getDataAll(db, storeName)
setDataSource(data)
}
async function addTableInfo(valueNow) {
const db = await IndexDB.openDB(dbName, storeName, 1)
await IndexDB.addData(db, storeName, valueNow)
setTimeout(() => {
getAll()
}, 1000);
}
async function updateTableInfo(valueNow) {
const db = await IndexDB.openDB(dbName, storeName, 1)
await IndexDB.updateDB(db, storeName, valueNow)
setTimeout(() => {
getAll()
}, 1000);
}
async function getItemByKey(key) {
const db = await IndexDB.openDB(dbName, storeName, 1)
var data = await IndexDB.getDataByKey(db, storeName, key)
Modal.confirm({
title: data.name,
content: JSON.stringify(data)
})
}
async function deleteKey(key) {
const db = await IndexDB.openDB(dbName, storeName, 1)
await IndexDB.deleteDB(db, storeName, key)
setTimeout(() => {
getAll()
}, 1000);
}
useEffect(() => {
getAll()
}, [])
const columns = [
{
title: 'id',
dataIndex: 'id',
},
{
title: '姓名',
dataIndex: 'name',
},
{
title: '年龄',
dataIndex: 'age',
},
{
title: '操作',
render: (text, record) => {
return <Space>
<a onClick={() => deleteKey(record.id)}>删除</a>
<ModalForm dataInfo={record} onOK={updateTableInfo} btnText={<a>修改</a>} />
<a onClick={() => getItemByKey(record.id)}>获取其他数据</a>
</Space>
}
}
];
return (
<Space direction='vertical' size='large'>
<Row >
<Space>
<Button onClick={createDB} type="primary">创建数据库</Button>
<Button onClick={async () => await IndexDB.deleteDBAll(dbName)} danger>删除数据库</Button>
</Space>
</Row>
<Row style={{ width: 1000 }}>
<Col span={24}>
<Card title='数据列表' extra={<ModalForm onOK={addTableInfo} />} >
<Table
dataSource={dataSource} columns={columns} />
</Card>
</Col>
</Row>
</Space>
)
}
ModalForm.jsx
jsx
import { Form, Input, Modal } from 'antd';
import { useState, useEffect } from 'react';
import { Button } from 'antd';
function ModalForm(props) {
const [form] = Form.useForm()
const [open, setOpen] = useState(false)
useEffect(() => {
if (open && props.dataInfo) {
form.setFieldsValue(props.dataInfo)
}
}, [open])
return (
<div>
<div onClick={() => setOpen(true)}>{props.btnText || <Button type='primary'>新增数据</Button>}</div>
<Modal
title="数据操作"
open={open}
destroyOnClose={true}
onOk={async () => {
const values = form.getFieldsValue()
await props.onOK(values)
setOpen(false)
}}
onCancel={() => {
setOpen(false)
}}
>
<Form form={form}>
<Form.Item label="主键" name='id'>
<Input placeholder="请输入" />
</Form.Item>
<Form.Item label="姓名" name='name'>
<Input placeholder="请输入" />
</Form.Item>
<Form.Item label="年龄" name='age'>
<Input placeholder="请输入" />
</Form.Item>
<Form.Item label="其他信息" name='desc'>
<Input placeholder="请输入" />
</Form.Item>
</Form>
</Modal>
</div>
)
}
export default ModalForm