IndexedDB教程
IndexedDB教程
一、概述
IndexedDB 是浏览器提供的本地数据库,js原生支持创建和操作 IndexedDB。IndexedDB 允许储存大量数据,提供查找接口,还能建立索引。就数据库类型而言,IndexedDB 不属于关系型数据库,更接近 NoSQL 数据库(存储key-value)。
它具有如下特点:
- 键值对储存。
- 异步。IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作。
- 支持事务。 IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。
- 同源限制。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。
- 存储空间大。在默认使用
短暂存储
情况下,一组域名受到组限制
,共享最小10MB,最大2GB的存储空间。 - 支持二进制存储。如 ArrayBuffer 对象和 Blob 对象
二、基本概念
IndexedDB 是一个比较复杂的 API,涉及不少概念。它把不同的实体,抽象成一个个对象接口。学习这个 API,就是学习它的各种对象接口。
* 数据库:IDBDatabase 对象
* 对象仓库:IDBObjectStore 对象
* 索引: IDBIndex 对象
* 事务: IDBTransaction 对象
* 操作请求:IDBRequest 对象
* 指针: IDBCursor 对象
* 主键集合:IDBKeyRange 对象
下面是一些主要的概念。
(1) 数据库
数据库是一系列相关数据的容器。每个域名(严格的说,是协议 + 域名 + 端口)都可以新建任意多个数据库。
IndexedDB 数据库有版本的概念。同一个时刻,只能有一个版本的数据库存在。如果要修改数据库结构(新增或删除object store
、索引或者主键),只能通过升级数据库版本完成。
(2) 对象仓库
每个数据库包含若干个对象仓库(object store)。它类似于关系型数据库的表。
(3) 数据记录
对象仓库保存的是数据记录。每条记录类似于关系型数据库的行,但是只有主键和数据体两部分。主键用来建立默认的索引,必须是不同的,否则会报错。主键可以是数据记录里面的一个属性,也可以指定为一个递增的整数编号。
{ id: 1, text: 'foo' }
上面是一个object store
中的一条数据,我们可以指定id
属性作为该object store
的主键,这条数据的数据体就是{ id: 1, text: 'foo' }
(4) 索引
为了加速数据的检索,可以在对象仓库里面,为不同的属性建立索引。
(5) 事务
数据的增删改查、数据库版本的升级都要通过事务完成,总共有三种事务模式:readwrite
、readonly
和versionchange
。
三、API 介绍
下面通过具体操作数据库时的流程,介绍相关 API。
3.1 打开数据库
使用 IndexedDB 的第一步是打开数据库,使用indexedDB.open()方法。
var request = window.indexedDB.open(databaseName, version);
这个方法接受两个参数,第一个参数是字符串,表示数据库的名字。如果指定的数据库不存在,就会新建数据库。第二个参数是整数,表示数据库的版本。如果省略,打开已有数据库时,默认为当前版本;新建数据库时,默认为1
indexedDB.open()
方法返回一个 IDBRequest 对象。这个对象通过三种事件error
、success
、upgradeneeded
,处理打开数据库的操作结果。error
、success
事件表示打开数据库失败、成功,如果数据库不存在或者指定打开的版本大于实际的数据库版本,就会触发数据库升级事件upgradeneeded
。这时通过事件对象的target.result
属性,拿到数据库实例。
var db;request.onsuccess = function (event) {db = request.result;console.log('数据库打开成功');
};request.onerror = function (event) {console.log('数据库打开报错');
};request.onupgradeneeded = function (event) {db = event.target.result;
}
注意:版本号是 unsigned long long 类型,不是浮点型,不能使用 2.4 作为版本号。
3.2 新建数据库
新建数据库与打开数据库是同一个操作。如果指定的数据库不存在,就会新建。不同之处在于,后续的操作主要在upgradeneeded事件的监听函数里面完成,因为这时版本从无到有,所以会触发这个事件。
通常,新建数据库以后,第一件事是新建对象仓库(即新建表)。
request.onupgradeneeded = function(event) {db = event.target.result;var objectStore = db.createObjectStore('person', { keyPath: 'id' });
}
上面代码中,数据库新建成功以后,新增一张叫做person的表格,主键是id。如果数据记录里面没有合适作为主键的属性,那么可以让 IndexedDB 自动生成主键。
var objectStore = db.createObjectStore('person',{ autoIncrement: true }
);
上面代码中,指定主键为一个递增的整数。
新建对象仓库以后,下一步可以新建索引。
request.onupgradeneeded = function(event) {db = event.target.result;var objectStore = db.createObjectStore('person', { keyPath: 'id' });objectStore.createIndex('name', 'name', { unique: false });objectStore.createIndex('email', 'email', { unique: true });
}
上面代码中,IDBObject.createIndex()
的三个参数分别为索引名称、索引所在的属性、配置对象(unique属性表示是否包含重复的值)。
3.3 数据操作
这里只介绍新增数据,其余操作与此类似,具体使用参考下一章节在项目中封装使用
新增数据指的是向对象仓库写入数据记录。这需要通过事务完成。
function add() {var request = db.transaction('person', 'readwrite').objectStore('person').add({ id: 1, name: '张三', age: 24, email: 'zhangsan@example.com' });request.onsuccess = function (event) {console.log('数据写入成功');};request.onerror = function (event) {console.log('数据写入失败');}
}add();
上面代码中,写入数据需要新建一个事务。新建时必须指定表格名称和操作模式(“只读"或"读写”)。新建事务以后,通过IDBTransaction.objectStore(name)
方法,拿到 IDBObjectStore
对象,再通过表格对象的add()
方法,向表格写入一条记录。
写入操作是一个异步操作,通过监听连接对象的success
事件和error
事件,了解是否写入成功。
3.4 使用索引
索引的意义在于,可以让你按任意字段搜索数据,也就是说从任意字段拿到数据记录。如果不建立索引,默认只能按主键搜索。
假定新建表格的时候,对name字段建立了索引。
objectStore.createIndex('name', 'name', { unique: false });
现在,就可以从name
找到对应的数据记录了。
var transaction = db.transaction('person', 'readonly');
var store = transaction.objectStore('person');
var index = store.index('name');
var request = index.get('李四');request.onsuccess = function (e) {var result = e.target.result;if (result) {// ...} else {// ...}
}
四、在项目中封装使用
4.1 打开(新建)数据库,DBInstance.ts: openDatabase
使用数据库的第一步就是打开或新建一个数据库
4.1.1 实例
创建一个学生数据库,其中有两个object store
。一个名为studentInfo
的object store
记录学生的身份信息,以学生的id
属性作为主键,同时对age
、name
字段建立索引。另一个名为score
的object store
记录学生的分数信息,自动递增生成主键。
enum StoreName {Student = 'studentInfo',Score = 'score'
}
const studentSchema: Array<DBStoreType> = [{dbStore: {dbStoreName: StoreName.Student,},dbIndex: [{dbIndexName: 'age',keyPath: 'age',},{dbIndexName: 'name',keyPath: 'name'}]},{dbStore: {dbStoreName: StoreName.Score,options: {autoIncrement: true}}}
]
const studentDatabase = await openDatabase('student', 1, studentSchema);
4.1.2 定义和用法
下面是openDatabase
函数的函数体。
openDatabase
函数返回一个 Promise 对象,可异步获取打开的数据库对象实例。如果打开的对象不存在或者数据库版本号比实际版本大,将会触发upgradeneeded
事件,在该事件中,首先删除旧有版本的所有object store
,然后建立新版本的数据库。
4.1.3 参数说明
dbName:string
数据库名。
dbVersion:number
数据库版本号。
dbStores:Array<DBStoreType>
数据库信息,可以有多个object store
,单个object store
可声明多个索引。
type DBStoreType = {dbStore: DBStoreParameter;dbIndexs?: Array<DBIndexParameter>;
}
type DBStoreParameter = {dbStoreName: string;options?: IDBObjectStoreParameters;
}
type DBIndexParameter = {dbIndexName: string;keyPath: string | string[];options?: IDBIndexParameters;
}
上面代码中,可以看到DBStoreType
由dbStore
和dbIndexs
属性组成。
4.2 Object Store 封装类,DBObjectStore.ts: DBObjectStore
4.2.1 实例
实例化DBObjectStore
,对studentDatabase
中的student
表进行各种数据操作,包括增删改查,迭代。
type StudentInfo = {id: number;name: string;age: number;
}
const StudentStore = DBObjectStore<[number],StudentInfo>
4.2.2 定义和用法
DBObjectStore
类初始化时,获得了数据库对象实例database
以及要操作的object store
名。
DBObjectStore
是一个泛型类,需要传递类型作为参数,以约束object store
中存储数据的key
,value
的类型。
注意:当不设置主键时,使用默认递增的主键,它的类型为 number。设置主键后,value 的类型必须为 object 类型,不能是 number, string 等类型。
4.2.3 DBObjectStore 方法介绍
getStore(storeName: string, mode? IDBTransactionMode)
返回 object store,准备开始操作数据,数据操作在指定模式下的事务中进行。不知道事务模式,默认为 readonly。
put(value: V, key? K): Promise<Event>
增加或修改数据,返回一个 Promise 对象,增加或修改成功传递 Event 事件参数回调。
增加还是修改取决于object store
中是否含有该键指向的数据。
如果设置了指定字段作为主键,那么就不需要使用第二个参数,value 中已包含该主键值。未指定主键,按默认主键,如果设置为主键递增增加,则第二参数可要可不要。
putBulk(value: Array<V>): Promise<Event>
批量一次性添加多条数据。返回一个 Promise 对象,成功则传递事件参数 Event 回调。
delete(query: K | IDBKeyRange): Promise<Event>
删除一条数据或多条数据。返回一个 Promise 对象,成功则传递事件参数 Event 回调。
get(query: K): Promise<V>
按主键查询一条数据。返回一个 Promise 对象,成功则传递查询结果回调。
getRange(query: IDBKeyRange): Promise<Array<V>>
按主键范围返回查询数据。返回一个 Promise 对象,成功则传递查询结果回调。
getAll(): Promise<Array<V>>
查询当前object store
的所有数据。返回一个 Promise 对象,成功则传递查询结果回调。
getByIndex(indexName: string, key: IDBValidKey): Promise<V>
按索引中的 key 查询一条数据。返回一个 Promise 对象,成功则传递查询结果回调。
getRangeByIndex(indexName: string, key: IDBKeyRange): Promise<Array<V>>
按索引中的 key范围查询多条数据。返回一个 Promise 对象,成功则传递查询结果回调。
iterate(iterateCall: (value: V) => void,query?:K | IDBValidKey | IDBKeyRange | null, indexName?: string, direction?: IDBCursorDirection): Promise<Array<V>>
迭代object store
或索引,可指定迭代范围,不指定迭代所有,也可指定迭代方向。迭代操作,通过传入的iterateCall
完成
clearData(): Promise<Event>
清空当前object store
中的数据。返回一个 Promise 对象,成功则传递事件参数 Event 回调。
五、数据库安全与管理
5.1 安全
IndexedDB遵守同源原则,这意味着某个源创建的数据库只能在该源里访问。但是也有特殊情况,下面这句话来自mdn
文档:
Third party window content (e.g. <iframe> content) cannot access IndexedDB if the browser is set to never accept third party cookies (see bug 1147821.)
这段英文不太能理解,另外我又在微软的技术文档里找到了另一种表述。
经过实验,发现是页面能访问 <iframe> 中的 indexedDB,但是<iframe>无法访问页面的 indexedDB。
5.2 数据库管理
indexedDB 创建的数据库内容始终是保留在用户端的,每当我们想改变数据库的结构,就要考虑用户端本地原来保存的是什么样的数据库。
最简单的方法,就是在数据库版本提升时,清空旧的 object store。这样当用户再次访问我们网站时,就会执行这段脚本,清空旧数据。
当某个数据库弃用时也需要,写一段脚本去删除。
(window as any)._indexedDB.deleteDatabase('databaseName');
数据库结构对应 openDatabase 函数的 dbStores:Array<DBStoreType> 参数,当其改变时,提升打开的数据库版本号以触发 onupgradeneeded 事件。
参考链接
- 浏览器数据库 IndexedDB 入门教程,阮一峰,https://www.ruanyifeng.com/blog/2018/07/indexeddb.html
- IndexedDB API,mdn web docs,https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API
IndexedDB教程相关推荐
- 前端每周清单第 33 期:React 16 发布与特性介绍,Expo AR 教程,ExtJS 从崛起到沉寂...
前端每周清单专注前端领域内容,以对外文资料的搜集为主,帮助开发者了解一周前端热点:分为新闻热点.开发教程.工程实践.深度阅读.开源项目.巅峰人生等栏目.欢迎关注[前端之巅]微信公众号(ID:front ...
- 《HTML5与CSS3实例教程》
<HTML5与CSS3实例教程> 基本信息 作者: (美)Brian P. Hogan 译者: 卢俊祥 丛书名: 图灵程序设计丛书 出版社:人民邮电出版社 ISBN:97871153634 ...
- 一行代码,搞定浏览器数据库 IndexedDB
作者 | 星尘starx 来源 | https://juejin.cn/post/6918705632757415950 前言 2021 年,如果你的前端应用,需要在浏览器上保存数据,有三个主流方案可 ...
- html5实践开发教程,HTML5基础与实践教程
HTML5基础与实践教程 语音 编辑 锁定 讨论 上传视频 <HTML5基础与实践教程>是2010年4月机械工业出版社出版的图书,作者是云翔,刘猛猛,欧阳植昊. 书 名 HTML5基 ...
- 本教程针对HBuilder5.0.0,制作日期2014-12-31(从HBuilder工具上获得)
/*注:本教程针对HBuilder5.0.0,制作日期2014-12-31*/ 创建HTML结构: h 8 (敲h激活代码块列表,按8选择第8个项目,即HTML代码块,或者敲h t Enter) ...
- 最实用的18个HTML5 API 教程大全,都在这里了
HTML5是web前端开发者必备的能力之一,公众号之前已经给同学们带来很多相关的教程,现在小编在这里将所有的API教程进行一次汇总,方便同学们学习. API详解1:实现fullscreen全屏模式 A ...
- cookie可存的最大限制_一文梳理Web存储,从cookie,WebStorage到IndexedDB
前言 HTTP是无状态的协议,网络早期最大的问题之一是如何管理状态.服务器无法知道两个请求是否来自同一个浏览器.cookie应运而生,开始出现在各大网站,然而随着前端应用复杂度的提高,Cookie 也 ...
- Chrome浏览器及调试教程
==>(微信公众号:IT知更鸟)欢迎关注<^>@<^> Chrome浏览器及调试教程 在web开发过程中,我们在写JavaScript脚本时难免会遇到各种bug,这时,我 ...
- Hbuilder快捷键教程
/*注:本教程针对HBuilder5.0.0,制作日期2014-12-31*/ 创建HTML结构: h 8 (敲h激活代码块列表,按8选择第8个项目,即HTML代码块,或者敲h t Enter) 中途 ...
最新文章
- vs中.exe运行闪退的解决办法
- token验证_java基于token的身份验证?读完之后,大部分程序员收藏了...
- Hibernate- 包作用详解
- 12v电流表的正确接法_难点分析 | 电表的内外接法
- vue项目封装axios
- 如何让cloudflare缓存html,CloudFlareCDN页面规则缓存设置教程
- linux的定cron计划任务命令
- 【PAT乙】1085 PAT单位排行 (25分) map排序
- 计算机丢失lame,libmp3lame64.dll
- 实验一 网络侦查与网络扫描
- Arcgis Engine 面的创建和设置
- php取tet文件内容,PHP中使用PDFlib TET提取PDF中的文本
- 通过微透镜阵列的传播
- 常用资源环境生态地理空间数据开源下载地址整理
- protues用一片74hc595控制两位数码管
- 物联网技术概论:1~7章汇总(西安交通大学)
- Github 镜像站的使用
- win10 安装 ros2.0---ROS Bouncy
- 【苹果家庭共享软件】设备安装iOS13增加了iMessage群发紧缩格式
- 变电站电源屏及温湿度和烟感设备协议接入流程记录
热门文章
- php 怎么提价redius,调价or涨价的英文怎么说?
- js判断是否是合法数字方法
- 关于逆元(费马小定理,exgcd)
- 聊聊高大上的物联网(智能家居)技术及平台
- 用户为什么收不到彩信手机报
- kotlin设置按钮不可点击_电脑护眼设置开启教程
- BootStrap_03(表单、按钮、图片、辅助类)
- 固件解包--binwalk分析
- window+kill+mysql慢查询_MySQL优化-(2)-慢查询日志工具-pt-query-digest
- 2022最新微信小程序授权登录(前后端分离)