前言
相信 IndexedDB 大家都有过了解,但是不一定每个人都有过实践,并且其中涉及到事务、游标等概念,会导致在初次使用时会有些不适应,那么本文会通过 IndexedDB 实现分页查询的形式进行实践,在开始之前,可以尝试思考一下浏览器的客户端存储你都了解哪些呢?
其实客户端存储分为下面几类:
cookie
Web Storage
sessionStorage
localStorage
IndexDB
cookie
cookies
的特点:
- cookie 是与特定域进行绑定的
- 所有 cookie 都会作为请求头部由浏览器发送给服务器
- 大多数浏览器对 1个域 下所有 cookie 的限制是不超过 4096 Byte(即 4 KB),上下可以有 1 Byte 的误差
假如 cookie 中保存大量信息,那么可能会影响特定域下浏览器请求的性能,因为保存的 cookie 越大,请求完成的时间就越长,由于以上的这些限制, cookie 只适合保存服务器必要的信息.
Web Storage
Web Storage 的目的是为了解决客户端需要使用 cookie 存储一些不需要频繁发送回服务器的数据.
sessionStorage
sessionStorage
的特点:
- 只存储会话数据,即数据只会存储到当前设置存储的 tab 页面被关闭或者是浏览器关闭
- 只能存储字符串类型数据,不能存储结构化数据
- 存储的数据不受页面刷新影响,可在浏览器崩溃并重启后恢复
- 大多数浏览器限制 1个源 只能存储 5MB
localStorage
localStorage
的特点:
- 存储的数据会一直保留,直到通过 JavaScript 删除或者用户清除浏览器缓存
- 只能存储字符串类型数据,不能存储结构化数据
- 存储的数据不受页面刷新影响,也不会因关闭窗口、标签页或重新启动浏览器而丢失
- 大多数浏览器限制 1个源 只能存储 5MB
IndexDB
IndexedDB(Indexed Database API) ,是浏览器中 存储结构化数据 的一个方案,用于代替目前已废弃的 Web SQL Database API.
其中 API 的设计基本上是 异步的,大多数操作以请求的形式 异步执行,产生成功的结果或错误都有对应的 onerror
和 onsuccess
事件处理程序来确定输出.
IndexedDB
的特点:
- 能够存储结构化数据
- IndexedDB 数据库是与 页面源 绑定的,不能跨域共享
- 大小限制
- Chrome 限制是每个源 5MB
- Firefox 限制是每个源 50MB
- Firefox(移动版) 限制每个源 5MB ,若超出则请求用户许可
从以上可以看出 IndexedDB 的限制其实 Web Storage 一样.
页面源(源)= 协议 + 域 + 端口
IndexedDB
数据库
IndexedDB 数据库是类似于 MySQL 或 Web SQL Database 的数据库,它使用对象存储数据而不是使用表格,并且属于 NoSQL 的风格.
建立数据库连接 —— indexedDB.open()
indexedDB.open(name, version)
其中 name 为数据库名称,version 为数据库版本,且版本只能为整数 通过indexedDB.open(name, version)
使用数据库:
- 如果给定名称的数据库 已存在,则会发送一个 打开请求
- 如果 不存在,则会发送 创建 并 打开 这个数据库的请求
- 异步执行,返回 IDBRequest 实例,需要监听
onerror
和onsuccess
事件
如下将创建一个名为 test 的数据库:
let db, request, version = 1; request = indexedDB.open("test", version); request.onerror = (event) => alert(`Failed to open: ${event.target.errorCode}`); request.onsuccess = (event) => { db = event.target.result; }; 复制代码
对象存储 —— request.onupgradeneeded()
数据库 版本发生变化 或 创建新数据库时 就会触发 onupgradeneeded
事件,此时需要指定或换修改 数据库模式 —— 包括数据库中的 对象存储 和对象 存储结构
如下将在 test 数据库下创建名为 productions 的对象存储模式
// 需要存储的对象 let production = { id: "20220128", name: "产品xxx", } // 指定对象存储的模式 request.onupgradeneeded = (event) => { const db = event.target.result; // 若存在就的存储结构则删除(可选) if (db.objectStoreNames.contains("productions")) { db.deleteObjectStore("productions"); } // 创建对象存储,keyPath 表示应该用作键存储对象的属性名 db.createObjectStore("productions", { keyPath: "id" }); }; 复制代码
事务 —— db.transaction()
IndexedDB 是基于事务的
创建对象存储之后,剩下的所有操作都是通过事务完成的,即若要 读写数据,都要通过事务 把所有修改操作进行组织.
创建事务
通过 let transaction = db.transaction()
方法来创建事务:
db.transaction()
— 不指定参数 则对数据库中所有 对象存储 有 只读 权限db.transaction("productions")
— 只加载特定对象存储的事务,只读 权限db.transaction(["productions","xxx"], "readwrite")
—— 加载多个对象存储的事务,且权限为 读写
transaction.onerror = (event) => { // 整个事务被取消 }; transaction.oncomplete = (event) => { // 整个事务成功完成 }; 复制代码
获取指定对象存储 —— transaction.objectStore()
通过 let store = transaction.objectStore("productions")
获取 productions 的对象存储,得到了对应的 对象存储 后,就可以使用以下方法:
store.get()
— 获取数据store.getAll()
— 获取所有数据store.add()
— 添加数据store.put()
— 更新数据store.delete()
— 删除数据store.clear()
— 清除数据- ...
写入对象
通过对象存储的引用 store
使用 store.add()
和 store.put()
往对象存储中写入数据,它们都只接收一个要存储的对象作为参数,前提是这个对象必须包含存储对象中的 keyPath
属性对应的属性字段.
对象存储中已存在 同名的键 时:
store.add()
导致错误store.put()
重写该对象
// productions 是一个产品数组 let request, requests = []; for (let product of productions) { request = store.add(product); request.onerror = () => { // 处理错误 }; request.onsuccess = () => { // 处理成功 }; requests.push(request); } 复制代码
查询数据
键查询 —— store.get()
键指的就是 onupgradeneeded
中指定的 keyPath
属性所对应的值,比如在上面我们指定了 { keyPath: 'id' }
,那么在使用键查询时,就必须要使用已存储数据对应属性的对应值.
假设需要获取如上图中的数据:
let storeName = "productions" // 先创建事务,通过事务获取指定的存储对象 let store = db.transaction(storeName, 'readwrite').objectStore(storeName) // 通过存储对象的 get 方法以及对应的 keyPath 键的 id 属性值获取数据 let request = store.get(1643527674720); request.onsuccess = (event) => { console.log(event.target.result); } request.onerror = (event) => { console.log(`query_key error with code:${event.target.errorCode}`); } 复制代码
索引查询 —— store.index(key).get(value)
对某些数据集可能需要为对象存储指定多个键,以便于后续查询数据时可以通过不同的键去获取,需要创建存储对象时使用 store.createIndex()
去创建索引,即 onupgradeneeded
事件处理程序中,如下创建了一个名为 price
的索引,后续查询数据时就可以使用这个所以名和对应的值进行查找
创建索引
request.onupgradeneeded = (event) => { db = event.target.result db.createObjectStore("productions", { keyPath: "id" }).createIndex("price", "price", { unique: false }); };