做项目的过程中,我们经常遇到需要把信息存储在本地的情况,比如权限验证的token、用户信息、埋点计数、客户配置的皮肤信息或语言种类等,我们可以暂存一下避免浏览器不必要的请求和客户多余操作,给客户使用带来方便。
随着浏览器的功能不断增强,越来越多的网站开始考虑,将大量数据储存在客户端,这样可以减少从服务器获取数据,直接从本地获取数据。
现有的浏览器数据储存方案,都不适合储存大量数据:Cookie 的大小不超过4KB,且每次请求都会发送回服务器;LocalStorage 在 2.5MB 到 10MB 之间(各家浏览器不同),而且不提供搜索功能,不能建立自定义的索引。所以,需要一种新的解决方案,这就是 IndexedDB 诞生的背景。
通俗地说,IndexedDB 就是浏览器提供的本地数据库,它可以被网页脚本创建和操作。IndexedDB 允许储存大量数据,提供查找接口,还能建立索引。这些都是 LocalStorage 所不具备的。就数据库类型而言,IndexedDB 不属于关系型数据库(不支持 SQL 查询语句),更接近 NoSQL 数据库。
(1)键值对储存。 IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以"键值对"的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。
(2)异步。 IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。
(3)支持事务。 IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。
(4)同源限制 IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。
(5)储存空间大 IndexedDB 的储存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有上限。
(6)支持二进制储存。 IndexedDB 不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象)。
IndexedDB 是一个比较复杂的 API,涉及不少概念。它把不同的实体,抽象成一个个对象接口。学习这个 API,就是学习它的各种对象接口。
下面是一些主要的概念。
(1)数据库
数据库是一系列相关数据的容器。每个域名(严格的说,是协议 + 域名 + 端口)都可以新建任意多个数据库。
IndexedDB 数据库有版本的概念。同一个时刻,只能有一个版本的数据库存在。如果要修改数据库结构(新增或删除表、索引或者主键),只能通过升级数据库版本完成。
(2)对象仓库
每个数据库包含若干个对象仓库(object store)。它类似于关系型数据库的表格。
(3)数据记录
对象仓库保存的是数据记录。每条记录类似于关系型数据库的行,但是只有主键和数据体两部分。主键用来建立默认的索引,必须是不同的,否则会报错。主键可以是数据记录里面的一个属性,也可以指定为一个递增的整数编号。
{ id: 1, text: 'foo' }
上面的对象中,id属性可以当作主键。
数据体可以是任意数据类型,不限于对象。
(4)索引
为了加速数据的检索,可以在对象仓库里面,为不同的属性建立索引。
(5)事务
数据记录的读写和删改,都要通过事务完成。事务对象提供error、abort和complete三个事件,用来监听操作结果。
IndexedDB 数据库的各种操作,一般是按照下面的流程进行的。这个部分只给出简单的代码示例,用于快速上手,详细的各个对象的 API 请看这里。
3.1 打开数据库
使用 IndexedDB 的第一步是打开数据库,使用indexedDB.open()方法。
var request=window.indexedDB.open(databaseName, version);
这个方法接受两个参数,第一个参数是字符串,表示数据库的名字。如果指定的数据库不存在,就会新建数据库。第二个参数是整数,表示数据库的版本。如果省略,打开已有数据库时,默认为当前版本;新建数据库时,默认为1。
indexedDB.open()方法返回一个 IDBRequest 对象。这个对象通过三种事件error、success、upgradeneeded,处理打开数据库的操作结果。
(1)error 事件
error事件表示打开数据库失败。
request.onerror=function (event) { console.log('数据库打开报错'); };
(2)success 事件
success事件表示成功打开数据库。
var db; request.onsuccess=function (event) { db=request.result; console.log('数据库打开成功'); };
这时,通过request对象的result属性拿到数据库对象。
(3)upgradeneeded 事件
如果指定的版本号,大于数据库的实际版本号,就会发生数据库升级事件upgradeneeded。
var db; request.onupgradeneeded=function (event) { db=event.target.result; }
这时通过事件对象的target.result属性,拿到数据库实例。
3.2 新建数据库
新建数据库与打开数据库是同一个操作。如果指定的数据库不存在,就会新建。不同之处在于,后续的操作主要在upgradeneeded事件的监听函数里面完成,因为这时版本从无到有,所以会触发这个事件。
通常,新建数据库以后,第一件事是新建对象仓库(即新建表)
request.onupgradeneeded=function(event) { db=event.target.result; var objectStore=db.createObjectStore('person', { keyPath: 'id' }); }
上面代码中,数据库新建成功以后,新增一张叫做person的表格,主键是id。
更好的写法是先判断一下,这张表格是否存在,如果不存在再新建。
request.onupgradeneeded=function (event) { db=event.target.result; var objectStore; if (!db.objectStoreNames.contains('person')) { objectStore=db.createObjectStore('person', { keyPath: 'id' }); } }
主键(key)是默认建立索引的属性。比如,数据记录是{ id: 1, name: '张三' },那么id属性可以作为主键。主键也可以指定为下一层对象的属性,比如{ foo: { bar: 'baz' } }的foo.bar也可以指定为主键。
如果数据记录里面没有合适作为主键的属性,那么可以让 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()的三个参数分别为索引名称、索引所在的属性、配置对象(说明该属性是否包含重复的值)。
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 读取数据
读取数据也是通过事务完成。
function read() { var transaction=db.transaction(['person']); var objectStore=transaction.objectStore('person'); var request=objectStore.get(1); request.onerror=function(event) { console.log('事务失败'); }; request.onsuccess=function( event) { if (request.result) { console.log('Name: ' + request.result.name); console.log('Age: ' + request.result.age); console.log('Email: ' + request.result.email); } else { console.log('未获得数据记录'); } }; } read();
上面代码中,objectStore.get()方法用于读取数据,参数是主键的值。
3.5 遍历数据
遍历数据表格的所有记录,要使用指针对象 IDBCursor。
function readAll() { var objectStore=db.transaction('person').objectStore('person'); objectStore.openCursor().onsuccess=function (event) { var cursor=event.target.result; if (cursor) { console.log('Id: ' + cursor.key); console.log('Name: ' + cursor.value.name); console.log('Age: ' + cursor.value.age); console.log('Email: ' + cursor.value.email); cursor.continue(); } else { console.log('没有更多数据了!'); } }; } readAll();
上面代码中,新建指针对象的openCursor()方法是一个异步操作,所以要监听success事件。
3.6 更新数据
更新数据要使用IDBObject.put()方法
function update() { var request=db.transaction(['person'], 'readwrite') .objectStore('person') .put({ id: 1, name: '李四', age: 35, email: 'lisi@example.com' }); request.onsuccess=function (event) { console.log('数据更新成功'); }; request.onerror=function (event) { console.log('数据更新失败'); } } update();
上面代码中,put()方法自动更新了主键为1的记录。
3.7 删除数据
IDBObjectStore.delete()方法用于删除记录
function remove() { var request=db.transaction(['person'], 'readwrite') .objectStore('person') .delete(1); request.onsuccess=function (event) { console.log('数据删除成功'); }; } remove();
3.8 使用索引
索引的意义在于,可以让你搜索任意字段,也就是说从任意字段拿到数据记录。如果不建立索引,默认只能搜索主键(即从主键取值)。
假定新建表格的时候,对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 { // ... } }
以上简单的介绍了IndexDB的使用API,大家是不是觉得东西太多太杂,使用起来并不方便,没问题,早有大神帮我们封装了IndexDB的功能,可以让我们更放心的使用
还能支持Typescript,更优雅
当然,IndexDB是现代浏览器的秘密武器,像什么IE6/7/8/9就老老实实用cookie吧,这里列出了兼容性(大家可以访问https://caniuse.com/#search=IndexedDB查看)
链接文章
http://www.ruanyifeng.com/blog/2018/07/indexeddb.html
https://www.cnblogs.com/huangenai/p/9766453.html
https://caniuse.com/
国党建网 https://www.ahdj.gov.cn/por/login_psw.csp?rnd=0.1195575743615227#https%3A%2F%2Fwww.ahdj.gov.cn%2F
共产党员网 http://www.12371.cn/
189邮箱 https://webmail30.189.cn/w2/
交党费 https://partyfee.bestpay.com.cn/sys/index
安徽本地发票验证 https://etax.anhui.chinatax.gov.cn/fpbl/view/fpxxCx
发票真伪查询 https://inv-veri.chinatax.gov.cn/
中共中央纪律检查委员会 http://www.ccdi.gov.cn/
共产党员网 http://www.12371.cn/
铁塔监控 http://180.153.49.130:9000/baf/jsp/uiframe/login.jsp
铁塔四费 http://120.52.120.164:8080/ahttreportweb/finedo/auth/loginindex
发票查验系统 https://www.51fapiao.cn/
天翼云省公司门户登录系统 https://cmp.ctyun.cn/cas/login
安徽电信官方招聘网址 http://www.zhaopin.ahtelecom.com.cn/
稳岗补贴 http://61.190.31.165:7006/index.html
养老网址 http://61.190.31.167:9009/volanst/logonDialog_267.jsp
统一社会信用代码查询 https://www.cods.org.cn/
智能广播管理平台 http://60.173.83.175:8090/ts/login.do
中国电信精品门店销售系统 https://jpqd.crm.189.cn/psm/
税务发票查验网址 https://etax.anhui.chinatax.gov.cn/fpbl/view/fpxxCx
阳光采购 https://caigou.chinatelecom.com.cn/MSS-PORTAL/account/login.do
全国党建网 https://www.ahdj.gov.cn/por/login_psw.csp?rnd=0.1195575743615227#https%3A%2F%2Fwww.ahdj.gov.cn%2F
国家税务总局全国增值税发票查验平台 https://inv-veri.chinatax.gov.cn/#
安徽省就业失业动态监测网上直报系统 http://124.127.118.122/anhuiss/
铜陵社保网上服务大厅 http://218.23.50.229:8015/index/index.html
安徽省阳光就业网 http://61.190.31.165:7006/
电商采购平台 https://www.ztf58.cn/login
安徽省招生考试院 https://www.ahzsks.cn/index.htm
安徽政务服务网 https://www.ahzwfw.gov.cn/
国家企业信用信息公示系统 http://www.gsxt.gov.cn/corp-query-homepage.html
安徽省应急管理厅 http://yjt.ah.gov.cn/
省消防救援总队 http://www.ah119.cn/ 51发票 https://www.51fapiao.cn/
翼支付缴党费系统 https://partyfee.bestpay.com.cn/sys/login
网厅代客下单 http://ah.189.cn/netup/bootkds/login?
安徽电信网上营业厅 http://www.189.cn/ah/
天翼积分商城 http://y.jf.189.cn/home/index.html
网厅电子发票打印页面 http://ah.189.cn/cms/r/cms/ah/default/v2016/electronicInvoice/elecInv.html 全国党员管理信息系统 https://ahdj.gov.cn/por/service.csp#
美橙域名查询(whois) https://whois.cndns.com/
社会组织代码证查询 https://www.cods.org.cn/
方便用人单位和高校毕业生、广大青年参加就业见习,近日,人力资源社会保障部上线全国统一的百万就业见习岗位募集计划服务平台,为供需双方搭建高效对接桥梁。
服务平台开通见习单位、见习岗位申请通道,分类发布热门企业、精选行业、分区域见习岗位,开设央企、名企、制造业、金融业、互联网等专栏,同时与各省服务平台互联互通,提供政策宣传、岗位发布、供需对接等一体化服务。自百万就业见习岗位募集计划3月份启动以来,共募集就业见习岗位40万个,后续服务平台还将持续推出更多见习岗位信息。
用人单位可登陆服务平台直接在线申请成为见习单位、设立见习岗位,也可以通过服务平台进入各省服务专区进行属地化申请,还可以联系本地公共就业人才服务机构进行线下咨询申请。高校毕业生、青年申请参加见习,可登陆服务平台根据企业类型、所属行业、见习地区等条件,查找心仪的见习岗位,发送个人简历进行申请;也可以通过服务平台进入各省服务专区在线申请,还可以联系本地公共就业人才服务机构进行线下咨询申请。
服务平台网址为http://zhaopin.ciic.com.cn。见习信息将同步在中国公共招聘网(http://job.mohrss.gov.cn)、高校毕业生就业服务平台(http://job.mohrss.gov.cn/202008gx/index.jhtml)、就业在线(https://www.jobonline.cn)等公共招聘平台发布。欢迎各类用人单位和高校毕业生、广大青年登陆服务平台参加就业见习!
*请认真填写需求信息,我们会在24小时内与您取得联系。