likes
comments
collection
share

跨浏览器存储IndexedDB增删改查查

作者站长头像
站长
· 阅读数 11

IndexedDB 是一种底层 API,用于在客户端存储大量的结构化数据(也包括文件/二进制大型对象(blobs))。该 API 使用索引实现对数据的高性能搜索

浏览器存储对比

特性cookiesessionStoragelocalStorageindexedDB
数据存储周期一般由服务器生成,可以设置过期时间; 前端采用和js-cookie等组件也可以生成页面关闭就清理刷新依然存在除非被清理,否则一直存在; 浏览器关闭还会保存在本地除非被清理,否则一直存在
是否支持跨页面
是否支持跨浏览器
数据存储大小4KSessionStorage默认能够存储5-10MB的数据根据当前浏览器的空闲空间来判断能够分配多少存储空间Safari: 5MB ;Chrome: 10MB ;Internet Explorer: 10MB;Mozilla Firefox: 10MB ;Opera: 10MB不限制大小
与服务端通信每次都会携带在请求的header 中,对于请求性能有影响; 同时由于请求中都带有,所以也容易出现安全问题不参与不参与不参与
特点字符串键值对在本地存储数据字符串键值对在本地存储数据字符串键值对在本地存储数据IndexedDB 是一个非关系型数据库(不支持通过 SQL 语句操作),用于在客户端存储大量的结构化数据(也包括文件/二进制大型对象(blobs))。该 API 使用索引实现对数据的高性能搜索

通过以上对比,我们如果需要使用到大数据存储的时候可以使用indexedDB进行处理,我们先看下浏览器的支持情况

浏览器支持情况

跨浏览器存储IndexedDB增删改查查

使用场景

IndexDB 是一种浏览器内置的数据库,它可以在浏览器中存储大量的数据,而不需要依赖于服务器。

  • 离线应用

IndexDB 可以在浏览器中存储大量的数据,这使得它非常适合用于离线应用。 当用户离线时,应用程序可以从IndexDB 中读取数据, 而不需要依赖于服务器。这使得应用程序可以在没有网络连接的情况下继续运行,从而提高了用户体验。

  • 部分网页支持下载离线应用,方便用户在没有网络的时候可以正常使用

跨浏览器存储IndexedDB增删改查查

跨浏览器存储IndexedDB增删改查查

  • 网页体验地址

developer.mozilla.org/zh-CN/docs/…

codesandbox.io/

  • 相关文档

developer.mozilla.org/zh-CN/docs/…

  • 缓存数据

IndexDB 可以用于缓存数据,这使得应用程序可以更快地加载数据。

当应用程序需要加载数据时,它可以首先从IndexDB中读取数据,

而不需要从服务器中获取数据。这可以大大减少网络请求的数量,

从而提高应用程序的性能。

-飞书文档的数据
  • 数据存储

IndexDB 可以用于存储大量的数据,这使得它非常适合用于需要存

储大量数据的应用程序。例如,一个在线笔记应用程序可以使用

IndexDB 来存储用户的笔记,而不需要依赖于服务器。

  • 数据同步

IndexDB 可以用于数据同步,这使得应用程序可以在多个设备之间

同步数据。当用户在一个设备上更新数据时,应用程序可以将数据

存储在 IndexDB 中,并将数据同步到其他设备上。这可以使用户在

不同设备上访问相同的数据,从而提高用户体验。

浏览器查看位置

  • 左侧为Chrome ,右侧为Safari 跨浏览器存储IndexedDB增删改查查

数据库操作

新增打开数据库

跨浏览器存储IndexedDB增删改查查

/**
 * 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 }) // 创建索引 可以让你搜索任意字段
            }
        }
    })
}

删除数据库

/**
 * 删除数据库
 */
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('数据库已关闭')
}

数据处理

新增数据

跨浏览器存储IndexedDB增删改查查

/**
 * 新增数据
 */
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)
        }
    })
}

通过主键读取数据

跨浏览器存储IndexedDB增删改查查

/**
 * 通过主键读取数据
 */
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)
        }
    })
}

更新数据

跨浏览器存储IndexedDB增删改查查

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)
        }
    })
}

获取全部数据

跨浏览器存储IndexedDB增删改查查

/**
 * 获取所有数据
 */
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)
        }
    })
}

第三方组件库

# idb

完整代码

indexDBTest源码

IndexDB.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

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


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