indexDB

Cookie

cookie可以做前端存储方案,但是有很多局限性。

  • 存储空间有限4k,
  • 存储有效时间有限制
  • 存在cookie中的数据,在每次请求的时候都会将它带上,使得每次请求的数据都会无意义增大
  • cookie设计之初并不是让前端存数据用的,它是为了让网站验证用户身份用的,存储功能只是一个手段而已

web Storage

  • 永久存储localStorage
  • 会话期间存储sessionStorage

优势

  • 存储空间更大,有5M大小
  • 在浏览器发送请求是不会带上web Storage里的数据
  • 更加友好的API
  • 可以做永久存储(localStorage)

局限

  • 随着web应用程序的不断发展,5M的存储大小对于一些大型的web应用程序来说有些不够
  • web Storage只能存储string类型的数据,对于object类型的数据只能先用JSON.stringify()转换下在存储

基于以上原因,提出了浏览器数据库存储,web SQL Database (已经被弃用) 和 IndexedDB(索引数据库)、

IndexedDB

介绍

IndexedDB是一种使用浏览器存储大量数据的方法,它创造的数据可以被查询,并且可以离线使用。

概念

使用IndexedDB,使用一个key索引,可以存储或者获取数据。是实务(transaction)中完成对数据的修改。遵从同源协议(same-origin policy)。所以只能访问同域中存储的数据,而不能访问其他域的

API包含异步(asynchronous)API 和同步(synchronous)API两种。异步API适合大多数情况,同步API必须同WebWorkers一同使用,目前,没有主流浏览器支持同步API。即使同步API被支持了,你也会在大多数情况使用异步API

IndexedDB是WebSQL数据库的取代品,W3C组织在2010年11月18日废弃了webSql.IndexedDB和WebSQL的不同点在于WebSQL是关系型数据库(复杂)IndexedDB是key-value型数据库(简单好使)

使用

创建一个IndexedDB数据库

// 创建数据库
// 第一个参数为数据库名称,第二个数据库为版本号,返回一个IDBOpenDBRequest对象用于操作数据库。
// 对于open()的第一个参数数据库名,open()会先去查找本地是否已有这个数据库,如果有则直接将这个数据库返回,如果没有,则先创建这个数据库,再返回。对于第二个参数版本号,则是一个可选参数,如果不传,默认为1,但是如果传入必须是一个整数
const request = indexedDB.open('myDatabase', 1)
request.addEventListener('success', e => {
    console.log('连接数据库成功')
})
request.addEventListener('error', e => {
    console.log('连接数据库失败')
})

创建一个对象仓库

在有了数据库之后,想要去存储数据,但是单只有数据库还不够,还需要有对象仓库(object store),对象仓库(object store)是IndexedDB数据库的基础,类似于MySQL中表的概念

要创建一个对象仓库必须在upgradeneeded事件中,而upgradeneeded事件只会在版本号更新的时候触发,这是因为IndexedDB API中不允许数据库中的数据仓库在同一版本中发送变化
// 监听upgradeneeded事件,在事件触发时使用createObjectStore()方法创建了一个对象仓库。createObjectStore()方法接受两个参数,第一个是对象仓库的名称,在同一个数据库中,仓库名不能重复,第二个参数是可选参数,用于指定数据的主键,以及是否自增主键
const request = indexedDB.open('myDatabase', 2)
request.addEventListener('upgradeneeded', e => {
    const db = e.target.result
    const store = db.createObjectStore('User', {keyPath: 'userId', autoIncrement: false});
    console.log('创建对象仓库成功')
})

创建事务

一个数据库事务通常包含了一个序列的对数据库的读/写操作。它的存在包含了两个以上目的
1、为数据库操作序列提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍然保持一致性的方法
2、当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作相互干扰

数据库事务四个特性,ACID特性

  • 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行
  • 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态,一致状态的含义是数据库中的数据应满足完整性约束
  • 隔离性(Isolation):多个事务并发执行时,一个事务的执行不影响其他事务的执行
  • 持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中
// transacton()来创建一个事务,transaction()接受两个参数,第一是要操作的对象仓库名称,第二个是你创建的事务模式。传入readonly时只能对对象仓库进行操作,无法写操作,可以传入readwrite进行读写操作
const request = indexedDB.open('myDatabase', 3)
request.addEventListener('success', e => {
    const db = e.target.result
    const tx = db.transaction('Users', 'readwrite')
})

操作数据

  • add():增加数据。接受一个参数,为需要保存到对象仓库中的对象
  • put():增加或修改数据。接受一个参数,为需要保存到对象仓库中的对象
  • get():获取数据。接受一个参数,为需要获取数据的主键值
  • delete():删除数据。接受一个参数,为需要获取数据的主键值
put保存数据时,该数据的主键在数据库中已经有相同主键的时候,则会修改数据库中对应主键的对象,而使用add保存数据,如果该主键已经存在,则保存失败

添加数据

const request = indexedDB.open('myDatabase', 3);
request.addEventListener('success', e => {
    const db = e.target.result;
    const tx = db.transaction('Users','readwrite');
    const store = tx.objectStore('Users');
    // 保存数据
    const reqAdd = store.add({'userId': 1, 'userName': '李白', 'age': 24});
    reqAdd.addEventListener('success', e => {
      console.log('保存成功')
    })
});

获取数据

const request = indexedDB.open('myDatabase', 3);
request.addEventListener('success', e => {
    const db = e.target.result;
    const tx = db.transaction('Users','readwrite');
    const store = tx.objectStore('Users');
    // 获取数据
    const reqGet = store.get(1);
    reqGet.addEventListener('success', e => {
      console.log(this.result.userName);    // 李白
    })
});

删除数据

const request = indexedDB.open('myDatabase', 3);
request.addEventListener('success', e => {
    const db = e.target.result;
    const tx = db.transaction('Users','readwrite');
    const store = tx.objectStore('Users');
    // 删除数据
    const reqDelete = store.delete(1);
    reqDelete.addEventListener('success', e => {
      console.log('删除数据成功');    // 李白
    })
});

使用游标

使用游标,一次性来获取一个区间内的数据

要使用游标,需要使用对象仓库上的openCursor()方法创建并打开。openCursor()方法接受两个参数

openCursor(range?: IDBKeyRange | number | string | Date | IDBArrayKey, direction?: IDBCursorDirection): IDBRequest;

第一个是范围,范围可以是一个IDBKeyRange对象,用以下方式创建

// boundRange 表示主键值从1到10(包含1和10)的集合。
// 如果第三个参数为true,则表示不包含最小键值1,如果第四参数为true,则表示不包含最大键值10,默认都为false
var boundRange = IDBKeyRange.bound(1, 10, false, false);

// onlyRange 表示由一个主键值的集合。only() 参数则为主键值,整数类型。
var onlyRange = IDBKeyRange.only(1);

// lowerRaneg 表示大于等于1的主键值的集合。
// 第二个参数可选,为true则表示不包含最小主键1,false则包含,默认为false
var lowerRange = IDBKeyRange.lowerBound(1, false);

// upperRange 表示小于等于10的主键值的集合。
// 第二个参数可选,为true则表示不包含最大主键10,false则包含,默认为false
var upperRange = IDBKeyRange.upperBound(10, false);

第二个参数是方向

  • next:游标中的数据按主键值升序排列,主键值相等的数据都被读取
  • nextunique:游标中的数据按主键值升序排列,主键值相等只读取第一条数据
  • prev:游标中的数据按主键值降序排列,主键值相等的数据都被读取
  • prevunique:游标中的数据按主键值降序排列,主键值相等只读取第一条数据
const request = indexedDB.open('myDatabase', 4);
request.addEventListener('success', e => {
    const db = e.target.result
    const tx = db.transaction('Users','readwrite');
    const store = tx.objectStore('Users');
    const range = IDBKeyRange.bound(1,10);
    const req = store.openCursor(range, 'next');
    req.addEventListener('success', e => {
      const cursor = this.result;
        if(cursor){
            console.log(cursor.value.userName);
            cursor.continue();
        }else{
            console.log('检索结束');
        }
    })
});

在上面的代码中如果检索到符合条件的数据时

使用cursor.value拿到数据.
使用cursor.updata()更新数据.
使用cursor.delete()删除数据.
使用cursor.continue()读取下一条数据.

索引

创建索引

使用对象仓库的createIndex()方法创建一个索引

createIndex(name: string, keyPath: string | string[], optionalParameters?: IDBIndexParameters): IDBIndex;

createIndex()方法接收三个参数

1、第一个参数name是索引名,不能重复

2、第二个参数keyPath是在存储对象上的那个属性上建立索引,可以是一个单个的key值,也可以是一个包含key值集合的数组

3、第三个参数optionalParameter是一个可选的对象参数{unique, multiEntry}

  • unique:用来指定索引值是否可以重复,为true代表不能相同,为false时代表可以相同
  • multiEntry:当第二个参数keyPath为一个数组时,如果multiEntry是true,则会以数组中的每个元素建立一条索引,如果是false,则以整个数组为keyPath值,添加一条索引

例子

//建立一条用户年龄的索引
const request = indexedDB.open('myDatabase', 5);
request.addEventListener('upgradeneeded', e => {
    const db = e.target.result;
    // 创建对象仓库
    const  store = db.createObjectStore('Users', {keyPath: 'userId', autoIncrement: false});
    const idx = store.createIndex('ageIndex','age',{unique: false})
});

使用索引

// 传入索引名称
const index = store.index('ageIndex')
// 拿到年龄在20岁以上的数据,升序排列
const request = indexedDB.open('myDatabase', 4);
request.addEventListener('success', e => {
    const db = e.target.result;
    const tx = db.transaction('Users','readwrite');
    const store = tx.objectStore('Users');
    const index = store.index('ageIndex');
    const req = index.openCursor(IDBKeyRange.lowerBound(20), 'next');
    req.addEventListener('success', e => {
      const cursor = e.target.result;
        if(cursor){
            console.log(cursor.value.age);
            cursor.continue();
        }else{
            console.log('检索结束');
        }
    })
});

indexedDB的兼容性

image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容

  •   支持离线 Web 应用开发是 HTML5 的另一个重点。   所谓离线 Web 应用,就是在设备不能上网的情况...
    霜天晓阅读 967评论 0 2
  • 转 # https://www.cnblogs.com/easypass/archive/2010/12/ 08/...
    吕品㗊阅读 9,617评论 0 44
  • ORA-00001: 违反唯一约束条件 (.) 错误说明:当在唯一索引所对应的列上键入重复值时,会触发此异常。 O...
    我想起个好名字阅读 4,866评论 0 9
  • #幸福是需要修出来的~每天进步1%~幸福实修14班~嘿,天鹅组贾双红# 20180302(39/60) 【幸福实修...
    幸福实修贾双红阅读 166评论 1 0
  • 01 当我满怀憧憬的进到大学以后发现所谓的大学并没有我想象中的那么美好。大学的女生宿舍少不了的是勾心斗角,今天谁惹...
    我的咖啡不加糖呀阅读 177评论 0 0