Web 存储

一、引入

​ 随着移动网络的发展与演化,“WebApp”在应用中占比也越来越大。WebApp 优异的性能表现,有一部分原因要归功于浏览器存储技术的提升。但是cookie存储数据的功能已经很难满足开发所需,逐渐被WebStorage、IndexedDB所取代。

二、cookie

1.cookie是什么?

​ Cookie是最早应用的前端存储,但是Cookie 的本职工作并非本地存储,而是“维持状态”。Cookie 其实就是一个存储在浏览器里的一个小小的文本文件,它附着在 HTTP 请求上,在浏览器和服务器之间“飞来飞去”。它可以携带用户信息,当服务器检查 Cookie 的时候,便可以获取到客户端的状态。

具体的cookie分析参考我的另一篇博客:https://blog.csdn.net/konghouy/article/details/83015236

2. cookie的缺陷

  • Cookie 不够大

    Cookie的大小限制在4KB左右,对于复杂的存储需求来说是不够用的。当 Cookie 超过 4KB 时,它将面临被裁切的命运。此外很多浏览器对一个站点的cookie个数也是有限制的。

    【注意】:各浏览器的cookie每一个name=value的value值大概在4k,所以4k并不是一个域名下所有的cookie共享的,而是一个name的大小。

  • 过多的 Cookie 会带来巨大的性能浪费

    Cookie 是紧跟域名的自动发送的。同一个域名下的所有请求,都会携带 Cookie。但是如果我们本次请求并不需要验证cookie(静态资源),但是不分青红皂白的直接发送,这是一件多么劳民伤财的事情。Cookie 虽然小,请求却可以有很多,随着请求的叠加,这样的不必要的 Cookie 带来的开销将是无法想象的。

    【解决方案】cookie是用来维护用户信息的,而域名(domain)下所有请求都会携带cookie,但对于静态文件的请求,携带cookie信息根本没有用,此时可以通过cdn(存储静态文件的)的域名和主站的域名分开来解决。

三、Web Storage

cookie本身就不是专门做存储的,为了弥补 Cookie 的局限性,让“专业的人做专业的事情”,Web Storage 出现了。之后cookie只做它应该做的事情了——作为客户端与服务器交互的通道,保持客户端状态。

1.LocalStorage

特点
  • 保存的数据长期存在,下一次访问该网站的时候,网页可以直接读取以前保存的数据;
  • 存储的值只能是字符串;
  • 没有过期时间,不会自动清除,除非代码强制清除,或手动清除;
  • 大小为5M左右;
  • 遵循同源策略,不同协议,不同域名,不同端口的两个页面无法共享存储内容;
  • 仅在客户端使用,不会自动与服务端进行通信;
  • 接口封装较好;
  • 在浏览器的隐私模式下面是不可读取的;
  • 如果存储内容多的话会消耗内存空间,会导致页面变卡;

基于上面的特点,LocalStorage可以作为浏览器本地缓存方案,用来提升网页首屏渲染速度(根据第一请求返回时,将一些不变信息直接存储在本地)。

操作

localStorage保存的数据,以“键值对”的形式存在。也就是说,每一项数据都有一个键名和对应的值。所有的数据都是以文本格式保存。

  • 存入数据使用setItem方法。它接受两个参数,第一个是键名,第二个是保存的数据。 原来有的属性名,使用此方法会进行覆盖。
window.localStorage.setItem("key","value");
  • 读取数据使用getItem方法。它只有一个参数,就是键名。 获取不存在的属性名会返回null。
var valueLocal = window.localStorage.getItem("key");
  • 移除数据使用removeItem方法。它只有一个参数,就是键名。删除不存在的属性名,不会产生效果。
window.localStorage.removeItem('myCat');
  • 清除当前域名的所以记录,使用clear方法。
window.localStorage.clear();
  • 用对象的方法进行localstorage操作
localStorage.a = 123;
console.log(localStorage.a);
//localStorage是window属性,可以省略window
//进行存入、修改、读取时可以使用对象的方法
//赋值为null,不会清除这个对象,而是赋值为'null',删除属性必须使用API
使用场景

​ LocalStorage在存储方面没有什么特别的限制,理论上 Cookie 无法胜任的、可以用简单的键值对来存取的数据存储任务,都可以交给 LocalStorage 来做。

​ 这里给大家举个例子,考虑到 LocalStorage 的特点之一是持久,有时我们更倾向于用它来存储一些内容稳定的资源。比如图片内容丰富的电商网站会用它来存储 Base64 格式的图片字符串。

2.sessionStorage

特点
  • 保存的数据用于浏览器的一次会话,当关闭浏览器存储内容就会消失;
  • sessionStorage 只在当前窗口有效,打开新窗口,获取不到其他窗口存储;
  • 大小为5M左右;
  • 仅在客户端使用,不会自动与服务端进行通信;
  • 接口封装较好。
操作

​ 与localStorage完全一致

使用场景

​ sessionStorage 更适合用来存储生命周期和它同步的会话级别的信息。这些信息只适用于当前会话,当你开启新的会话时,它也需要相应的更新或释放。

​ 比如微博的 sessionStorage就主要是存储你本次会话的浏览足迹:lasturl 对应的就是你上一次访问的 URL 地址,这个地址是即时的。当你切换 URL 时,它随之更新,当你关闭页面时,留着它也确实没有什么意义了,干脆释放吧。这样的数据用 sessionStorage 来处理再合适不过。

​ sessionStorage 也可以有效对表单信息进行维护,刷新的时候,sessionStorage并不会清除,但是html会进行重新加载,导致表单数据发生了丢失,使用sessionStorage信息重写表单,保证表单信息不丢失。

3.indexedDB

​ 说到底,Web Storage 是对 Cookie 的拓展,它只能用于存储少量的简单数据。当遇到大规模的、结构复杂的数据时,Web Storage 也爱莫能助了。这时候我们就要清楚我们的终极大 boss——IndexedDB!

​ IndexedDB 用于客户端存储大量结构化数据(包括文件和blobs)。该API使用索引来实现对该数据的高性能搜索。IndexedDB 是一个运行在浏览器上的非关系型数据库。既然是数据库了,那就不是 5M、10M 这样小打小闹级别了。理论上来说,IndexedDB 是没有存储上限的(一般来说不会小于 250M)。它不仅可以存储字符串,还可以存储二进制数据。通过测试我的电脑上的indexDB存储容量高达10392MB。

特点
  • 键值对储存

IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以"键值对"的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。

  • 异步

IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。

  • 支持事务

IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。

  • 同源限制

IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。这里的只是域名上的跨越,非端口号也可以共享。

  • 储存空间大

IndexedDB 的储存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有上限。

  • 支持二进制储存

IndexedDB 不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象)。

操作

​ 在IndexedDB大部分操作并不是我们常用的调用方法,而是回调函数的模式。也就是indexDB的所以操作都是下发指令,异步读写,监控结束执行回调来实现的。


1.IDBRequest 对象

IDBRequest 对象表示打开数据库连接请求,indexedDB.open()方法和indexedDB.deleteDatabase()方法会返回这个对象,对象具体是IDBOpenDBRequestIDBRequestIDBOpenDBRequest的原型)。数据库的操作都是通过这个对象完成的。

这个对象的所有操作都是异步操作,要通过readyState属性判断是否完成,如果为pending就表示操作正在进行,如果为done就表示操作完成,可能成功也可能失败。

操作完成以后,触发success事件或error事件,这时可以通过result属性和error属性拿到操作结果。如果在pending阶段,就去读取这两个属性,是会报错的。

  • IDBRequest 对象有以下属性。

    • IDBRequest.readyState:等于pending表示操作正在进行,等于done表示操作正在完成。
    • IDBRequest.result:返回请求的结果。如果请求失败、结果不可用,读取该属性会报错。
    • IDBRequest.error:请求失败时,返回错误对象。
    • IDBRequest.source:返回请求的来源(比如索引对象或 ObjectStore)。
    • IDBRequest.transaction:返回当前请求正在进行的事务,如果不包含事务,返回null
    • IDBRequest.onsuccess:指定success事件的监听函数。
    • IDBRequest.onerror:指定error事件的监听函数。

    IDBOpenDBRequest 对象继承了 IDBRequest 对象,提供了两个额外的事件监听属性。

    • IDBOpenDBRequest.onblocked:指定blocked事件(upgradeneeded事件触发时,数据库仍然在使用)的监听函数。
    • IDBOpenDBRequest.onupgradeneededupgradeneeded事件的监听函数。

2.建立/打开数据库
  • IndexedDB——window.indexedDB.open("testDB")
function openDB(name) {
	var request = window.indexedDB.open(name) 
    //建立打开IndexedDB

    request.onerror = function(e) {
        console.log('open indexdb error')
        //数据库打开失败
    }
    request.onsuccess = function(e) {
        myDB.db = e.target.result 
        //打开成功,这就是IndexedDB对象
        //这个也是数据库对象:request.result;
    }
    request.onupgradeneeded = function (e) {
  		myDB.db = e.target.result;
        //如果指定的版本号,大于数据库的实际版本号
        //就会发生数据库升级事件upgradeneeded
	}
    
    request.onblocked = function(e){
        console.log("数据库未关闭");
        //当打开两个页面,访问同一个数据库,
        //一个页面的版本超过,另一个页面的版本时,会触发这个事件
    }
}
var myDB = {
	name: 'testDB',
	version: '1',
	db: null
}
openDB(myDB.name,myDB.version);

​ open命令传入的是两个参数,一个是打开的数据库名称,另一个是数据库版本号。如果指定的数据库不存在,就会新建数据库。打开已有数据库时,默认为当前版本;新建数据库时,默认为1。一般只有需要修改数据库的存储结构的时,才需要更新数据库的版本。一般的增删改查等基本操作不用进行版本演进。

【问题】:

​ 在这里我注意到一个问题,即indexDB.open()返回的并不是一个数据库对象,而是一个数据库打开请求对象,当拿到这个请求对象,紧接着就是绑定成功、失败和升级的回调函数,等待回调函数的执行。那么究竟什么时候真正触发了数据库的打开操作呢?会不会出现回调函数还没有绑定成功,但是数据库库已经打开,导致回调函数没有执行?

​ 通过实际测试,发现数据库打开操作,相当于添加到了js的任务队列中,这个操作一定会等到,当前js任务队列都已经执行完才会触发。


3.删除数据库
  • window.indexedDB.deleteDatabase("testDB")
var DBDeleteRequest = window.indexedDB.deleteDatabase('demo');

DBDeleteRequest.onerror = function (event) {
  console.log('Error');
};

DBDeleteRequest.onsuccess = function (event) {
  console.log('success');
};

【注意】:

  • 调用deleteDatabase()方法以后,当前数据库的其他已经打开的连接都会接收到versionchange事件。
  • onblocked是针对正在操作的数据库,监控的事件
  • onversionchange是针对已经打开的数据库,监控的事件

4.IDBDatabase 对象

打开数据成功以后,可以从IDBOpenDBRequest对象的result属性上面,拿到一个IDBDatabase对象,它表示连接的数据库。后面对数据库的操作,都通过这个对象完成。

var db;
var DBOpenRequest = window.indexedDB.open('demo', 1);

DBOpenRequest.onerror = function (event) {
  console.log('Error');
};

DBOpenRequest.onsuccess = function(event) {
  db = DBOpenRequest.result;
  // ...
};
  • IDBDatabase 属性
    • IDBDatabase.name:字符串,数据库名称。
    • IDBDatabase.version:整数,数据库版本。数据库第一次创建时,该属性为空字符串。
    • IDBDatabase.objectStoreNames:DOMStringList 对象(字符串的集合),包含当前数据的所有 object store 的名字。
    • IDBDatabase.onabort:指定 abort 事件(事务中止)的监听函数。
    • IDBDatabase.onclose:指定 close 事件(数据库意外关闭)的监听函数。
    • IDBDatabase.onerror:指定 error 事件(访问数据库失败)的监听函数。
    • IDBDatabase.onversionchange:数据库版本变化时触发(发生upgradeneeded事件,或调用indexedDB.deleteDatabase())。

5.新建对象仓库

下面是createObjectStore()方法的例子。

var request = window.indexedDB.open('demo', 2);

request.onupgradeneeded = function (event) {
  var db = event.target.result;

  db.onerror = function(event) {
    console.log('error');
  };

    var objectStore = db.createObjectStore('items'{
                                           keyPath:“56”,
                         
                                           });

  // ...
};

【注意】:

​ 1、由于创建、删除数据库对象仓库涉及到数据的结构的变化,所以相关代码需要在onupgradeneeded下调用

​ 2、如果该对象仓库已经存在,就会抛出一个错误。为了避免出错,需要用到objectStoreNames属性,检查已有哪些对象仓库。

​ 3、上面代码中,keyPath属性表示主键(由于主键的值不能重复,所以上例存入之前,必须保证数据的email属性值都是不一样的),默认值为nullautoIncrement属性表示,是否使用自动递增的整数作为主键(第一个数据记录为1,第二个数据记录为2,以此类推),默认为false。一般来说,keyPathautoIncrement属性只要使用一个就够了,如果两个同时使用,表示主键为递增的整数,且对象不得缺少keyPath指定的属性。

​ 4、当数据库指定了keyPath后,添加是value必须是包含keyPath属性的对象。同时不用指定key,key自动从对象中提取。

​ 5、当数据库指定了autoIncrement后,add时可以不用添加key(也可以指定),key会进行自增。


6.删除对象仓库
  • 下面是deleteObjectStore()方法的例子。
var dbName = 'sampleDB';
var dbVersion = 2;
var request = indexedDB.open(dbName, dbVersion);

request.onupgradeneeded = function(e) {
  var db = request.result;
  if (e.oldVersion < 1) {
    db.createObjectStore('store1');
  }

  if (e.oldVersion < 2) {
    db.deleteObjectStore('store1');
    db.createObjectStore('store2');
  }

  // ...
};


7.IDBObjectStore 对象

IDBObjectStore指的是对象仓库,类似于关系数据库中的表。

IDBDatabase 对象的transaction()返回一个事务对象,事务对象的objectStore()方法返回 IDBObjectStore 对象,因此可以采用下面的链式写法。

db.transaction(['test'], 'readonly')
  .objectStore('test')
  .get(X)
  .onsuccess = function (e) {}

属性

IDBObjectStore 对象有以下属性。

  • IDBObjectStore.indexNames:返回一个类似数组的对象(DOMStringList),包含了当前对象仓库的所有索引。
  • IDBObjectStore.keyPath:返回当前对象仓库的主键。
  • IDBObjectStore.name:返回当前对象仓库的名称。
  • IDBObjectStore.transaction:返回当前对象仓库所属的事务对象。
  • IDBObjectStore.autoIncrement:布尔值,表示主键是否会自动递增。

方法

IDBObjectStore 对象有以下方法。

(1)IDBObjectStore.add()

IDBObjectStore.add()用于向对象仓库添加数据,返回一个IDBRequest 对象。该方法只用于添加数据,如果主键相同会报错,因此更新数据必须使用put()方法。

objectStore.add(value, key)

该方法接受两个参数,第一个参数是键值,第二个参数是主键,该参数可选,如果省略默认为null

创建事务以后,就可以获取对象仓库,然后使用add()方法往里面添加数据了。

var db;
var DBOpenRequest = window.indexedDB.open('demo', 1);

DBOpenRequest.onsuccess = function (event) {
  db = DBOpenRequest.result;
  var transaction = db.transaction(['items'], 'readwrite');

  transaction.oncomplete = function (event) {
    console.log('transaction success');
  };

  transaction.onerror = function (event) {
    console.log('transaction error: ' + transaction.error);
  };

  var objectStore = transaction.objectStore('items');
  var objectStoreRequest = objectStore.add({ foo: 1 });

  objectStoreRequest.onsuccess = function (event) {
    console.log('add data success');
  };

};

(2)IDBObjectStore.put()

IDBObjectStore.put()方法用于更新某个主键对应的数据记录,如果对应的键值不存在,则插入一条新的记录。该方法返回一个 IDBRequest 对象。

objectStore.put(item, key)

该方法接受两个参数,第一个参数为新数据,第二个参数为主键,该参数可选,且只在自动递增时才有必要提供,因为那时主键不包含在数据值里面。

(3)IDBObjectStore.clear()

IDBObjectStore.clear()删除当前对象仓库的所有记录。该方法返回一个 IDBRequest 对象。

objectStore.clear()

该方法不需要参数。

(4)IDBObjectStore.delete()

IDBObjectStore.delete()方法用于删除指定主键的记录。该方法返回一个 IDBRequest 对象。

objectStore.delete(Key)

该方法的参数为主键的值。

(5)IDBObjectStore.count()

IDBObjectStore.count()方法用于计算记录的数量。该方法返回一个 IDBRequest 对象。

IDBObjectStore.count(key)

不带参数时,该方法返回当前对象仓库的所有记录数量。如果主键或 IDBKeyRange 对象作为参数,则返回对应的记录数量。

(6)IDBObjectStore.getKey()

IDBObjectStore.getKey()用于获取主键。该方法返回一个 IDBRequest 对象。

objectStore.getKey(key)

该方法的参数可以是主键值或 IDBKeyRange 对象。

(7)IDBObjectStore.get()

IDBObjectStore.get()用于获取主键对应的数据记录。该方法返回一个 IDBRequest 对象。

objectStore.get(key)

(8)IDBObjectStore.getAll()

DBObjectStore.getAll()用于获取对象仓库的记录。该方法返回一个 IDBRequest 对象。

// 获取所有记录
objectStore.getAll()

// 获取所有符合指定主键或 IDBKeyRange 的记录
objectStore.getAll(query)

// 指定获取记录的数量
objectStore.getAll(query, count)

(9)IDBObjectStore.getAllKeys()

IDBObjectStore.getAllKeys()用于获取所有符合条件的主键。该方法返回一个 IDBRequest 对象。

// 获取所有记录的主键
objectStore.getAllKeys()

// 获取所有符合条件的主键
objectStore.getAllKeys(query)

// 指定获取主键的数量
objectStore.getAllKeys(query, count)

(10)IDBObjectStore.index()

IDBObjectStore.index()方法返回指定名称的索引对象 IDBIndex。

objectStore.index(name)

有了索引以后,就可以针对索引所在的属性读取数据。

var t = db.transaction(['people'], 'readonly');
var store = t.objectStore('people');
var index = store.index('name');

var request = index.get('foo');

上面代码打开对象仓库以后,先用index()方法指定获取name属性的索引,然后用get()方法读取某个name属性(foo)对应的数据。如果name属性不是对应唯一值,这时get()方法有可能取回多个数据对象。另外,get()是异步方法,读取成功以后,只能在success事件的监听函数中处理数据。

要有一些关于index的用法,请参照IDBIndex 对象。

(13)IDBObjectStore.openCursor()

IDBObjectStore.openCursor()用于获取一个指针对象。

IDBObjectStore.openCursor()

指针对象可以用来遍历数据。该对象也是异步的,有自己的successerror事件,可以对它们指定监听函数。

var t = db.transaction(['test'], 'readonly');
var store = t.objectStore('test');

var cursor = store.openCursor();

cursor.onsuccess = function (event) {
  var res = event.target.result;
  if (res) {
    console.log('Key', res.key);
    console.dir('Data', res.value);
    res.continue();
  }
}

监听函数接受一个事件对象作为参数,该对象的target.result属性指向当前数据记录。该记录的keyvalue分别返回主键和键值(即实际存入的数据)。continue()方法将光标移到下一个数据对象,如果当前数据对象已经是最后一个数据了,则光标指向null

openCursor()方法的第一个参数是主键值,或者一个 IDBKeyRange 对象。如果指定该参数,将只处理包含指定主键的记录;如果省略,将处理所有的记录。该方法还可以接受第二个参数,表示遍历方向,默认值为next,其他可能的值为prevnextuniqueprevunique。后两个值表示如果遇到重复值,会自动跳过。

(14)IDBObjectStore.openKeyCursor()

IDBObjectStore.openKeyCursor()用于获取一个主键指针对象。

IDBObjectStore.openKeyCursor()

8.IDBTransaction 对象

​ IDBTransaction 对象用来异步操作数据库事务,所有的读写操作都要通过这个对象进行。操作流程:创建事件,通过事件打开对象仓库,对仓库进行操作,操作时会生成IDBRequest对象,通过监控IDBRequest的onsuccess和onerror来判断一条操作是否生效。通过监控IDBTransaction的onsuccess和onerror来判断事务是否生效。

IDBTransaction 对象有以下属性

  • IDBTransaction.db:返回当前事务所在的数据库对象 IDBDatabase。
  • IDBTransaction.error:返回当前事务的错误。如果事务没有结束,或者事务成功结束,或者被手动终止,该方法返回null
  • IDBTransaction.mode:返回当前事务的模式,默认是readonly(只读),另一个值是readwrite
  • IDBTransaction.objectStoreNames:返回一个类似数组的对象 DOMStringList,成员是当前事务涉及的对象仓库的名字。
  • IDBTransaction.onabort:指定abort事件(事务中断)的监听函数。监控abort()的触发时间。
  • IDBTransaction.oncomplete:指定complete事件(事务成功)的监听函数。
  • IDBTransaction.onerror:指定error事件(事务失败)的监听函数。

IDBTransaction 对象有以下方法。

  • IDBTransaction.abort():终止当前事务,回滚所有已经进行的变更。
  • IDBTransaction.objectStore(name):返回指定名称的对象仓库 IDBObjectStore。

通过事务维护数据库的完整性。如果在某一步操作出现异常,通过事务回滚保证数据库不出现异常。

9.IDBIndex 对象

由于indexedDB存储数据时,使用的是键值对来进行数据的存储,而存储的主体很多情况下都是对象,之前的主键设置中可以将对象中的某一个属性,指定为主键,但是很多情况下我们需要筛选更对的属性。这时候我们就需要建立起索引来帮助我们进行数据的管理。

索引需要在创建对象仓库时就要一起创建好,这个操作同样也需要在onupgradeneeded中调用。索引本质上就是一个虚表,只是从对象的另一个属性上,来进行数据的组织和管理。

IDBIndex 是持久性的键值对存储。只要插入、更新或删除数据记录,引用的对象库中的记录,索引就会自动更新。这就要求在对象存储时,对象一定要主键和索引指定的属性。

(1)IDBObjectStore.createIndex()

IDBObjectStore.createIndex()方法用于新建当前数据库的一个索引。该方法只能在VersionChange监听函数里面调用。

objectStore.createIndex(indexName, keyPath, objectParameters)

该方法可以接受三个参数。

  • indexName:索引名
  • keyPath:主键
  • objectParameters:配置对象(可选)

第三个参数可以配置以下属性。

  • unique:如果设为true,将不允许重复的值
  • multiEntry:如果设为true,对于有多个值的主键数组,每个值将在索引里面新建一个条目,否则主键数组对应一个条目。

假定对象仓库中的数据记录都是如下的person类型。

var person = {
  name: name,
  email: email,
  created: new Date()
};

可以指定这个对象的某个属性来建立索引。

var store = db.createObjectStore('people', { autoIncrement: true });

store.createIndex('name', 'name', { unique: false });
store.createIndex('email', 'email', { unique: true });

上面代码告诉索引对象,name属性不是唯一值,email属性是唯一值。

(2)IDBObjectStore.deleteIndex()

IDBObjectStore.deleteIndex()方法用于删除指定的索引。该方法只能在VersionChange监听函数里面调用。

objectStore.deleteIndex(indexName)

IDBIndex 对象有以下属性

  • IDBIndex.name:字符串,索引的名称。
  • IDBIndex.objectStore:索引所在的对象仓库。
  • IDBIndex.keyPath:索引的主键。
  • IDBIndex.multiEntry:布尔值,针对keyPath为数组的情况,如果设为true,创建数组时,每个数组成员都会有一个条目,否则每个数组都只有一个条目。
  • IDBIndex.unique:布尔值,表示创建索引时是否允许相同的主键。

IDBIndex 对象有以下方法,它们都是异步的,立即返回的都是一个 IDBRequest 对象。

  • IDBIndex.count():用来获取记录的数量。它可以接受主键或 IDBKeyRange 对象作为参数,这时只返回符合主键的记录数量,否则返回所有记录的数量。
  • IDBIndex.get(key):用来获取符合指定主键的数据记录。
  • IDBIndex.getKey(key):用来获取指定的主键。
  • IDBIndex.getAll():用来获取所有的数据记录。它可以接受两个参数,都是可选的,第一个参数用来指定主键,第二个参数用来指定返回记录的数量。如果省略这两个参数,则返回所有记录。由于获取成功时,浏览器必须生成所有对象,所以对性能有影响。如果数据集比较大,建议使用 IDBCursor 对象。
  • IDBIndex.getAllKeys():该方法与IDBIndex.getAll()方法相似,区别是获取所有主键。
  • IDBIndex.openCursor():用来获取一个 IDBCursor 对象,用来遍历索引里面的所有条目。
  • IDBIndex.openKeyCursor():该方法与IDBIndex.openCursor()方法相似,区别是遍历所有条目的主键。
10.IDBCursor 对象

IDBCursor 对象代表指针对象,用来遍历数据仓库(IDBObjectStore)或索引(IDBIndex)的记录。

IDBCursor 对象一般通过IDBObjectStore.openCursor()IDBIndex.openCursor()方法获得。

var objectStore = transaction.objectStore('items');
var index = objectStore.index("name");

var request = index.openCursor(1,'next');

request.onsuccess = function(event) {
    var cur = event.target.result;
    cur.advance(1);
    console.log(cur);
};

ObjectStore.openCursor();有两个参数,第一个参数是“键”,第二个是遍历模式。openCursor()方法返回的是一个请求对象,通过监控请求的success来获取指针。当操作指针移动时,都会再次触发success函数。

IDBCursor 对象的属性。

  • IDBCursor.source:返回正在遍历的对象仓库或索引。
  • IDBCursor.direction:字符串,表示指针遍历的方向。共有四个可能的值:next(从头开始向后遍历)、nextunique(从头开始向后遍历,重复的值只遍历一次)、prev(从尾部开始向前遍历)、prevunique(从尾部开始向前遍历,重复的值只遍历一次)。该属性通过IDBObjectStore.openCursor()方法的第二个参数指定,一旦指定就不能改变了。
  • IDBCursor.key:返回当前记录的主键。
  • IDBCursor.value:返回当前记录的数据值。
  • IDBCursor.primaryKey:返回当前记录的主键。对于数据仓库(objectStore)来说,这个属性等同于 IDBCursor.key;对于索引,IDBCursor.key 返回索引的位置值,该属性返回数据记录的主键。

IDBCursor 对象有如下方法。

  • IDBCursor.advance(n):指针向前移动 n 个位置。
  • IDBCursor.continue():指针向前移动一个位置。它可以接受一个主键作为参数,这时会跳转到这个主键。
  • IDBCursor.continuePrimaryKey():该方法需要两个参数,第一个是key,第二个是primaryKey,将指针移到符合这两个参数的位置。
  • IDBCursor.delete():用来删除当前位置的记录,返回一个 IDBRequest 对象。该方法不会改变指针的位置。
  • IDBCursor.update():用来更新当前位置的记录,返回一个 IDBRequest 对象。它的参数是要写入数据库的新的值。
11.IDBKeyRange 对象

IDBKeyRange 对象代表数据仓库(object store)里面的一组主键。根据这组主键,可以获取数据仓库或索引里面的一组记录。

IDBKeyRange 可以只包含一个值,也可以指定上限和下限。它有四个静态方法,用来指定主键的范围。

  • IDBKeyRange.lowerBound():指定下限。
  • IDBKeyRange.upperBound():指定上限。
  • IDBKeyRange.bound():同时指定上下限。
  • IDBKeyRange.only():指定只包含一个值。

下面是一些代码实例。

// All keys ≤ x
var r1 = IDBKeyRange.upperBound(x);

// All keys < x
var r2 = IDBKeyRange.upperBound(x, true);

// All keys ≥ y
var r3 = IDBKeyRange.lowerBound(y);

// All keys > y
var r4 = IDBKeyRange.lowerBound(y, true);

// All keys ≥ x && ≤ y
var r5 = IDBKeyRange.bound(x, y);

// All keys > x &&< y
var r6 = IDBKeyRange.bound(x, y, true, true);

// All keys > x && ≤ y
var r7 = IDBKeyRange.bound(x, y, true, false);

// All keys ≥ x &&< y
var r8 = IDBKeyRange.bound(x, y, false, true);

// The key = z
var r9 = IDBKeyRange.only(z);

IDBKeyRange.lowerBound()IDBKeyRange.upperBound()IDBKeyRange.bound()这三个方法默认包括端点值,可以传入一个布尔值,修改这个属性。

与之对应,IDBKeyRange 对象有四个只读属性。

  • IDBKeyRange.lower:返回下限
  • IDBKeyRange.lowerOpen:布尔值,表示下限是否为开区间(即下限是否排除在范围之外)
  • IDBKeyRange.upper:返回上限
  • IDBKeyRange.upperOpen:布尔值,表示上限是否为开区间(即上限是否排除在范围之外)

IDBKeyRange 实例对象生成以后,将它作为参数输入 IDBObjectStore 或 IDBIndex 对象的openCursor()方法,就可以在所设定的范围内读取数据。

var t = db.transaction(['people'], 'readonly');
var store = t.objectStore('people');
var index = store.index('name');

var range = IDBKeyRange.bound('B', 'D');

index.openCursor(range).onsuccess = function (e) {
  var cursor = e.target.result;
  if (cursor) {
    console.log(cursor.key + ':');

    for (var field in cursor.value) {
      console.log(cursor.value[field]);
    }
    cursor.continue();
  }
}

IDBKeyRange 有一个实例方法includes(key),返回一个布尔值,表示某个主键是否包含在当前这个主键组之内。

var keyRangeValue = IDBKeyRange.bound('A', 'K', false, false);

keyRangeValue.includes('F') // true
keyRangeValue.includes('W') // false

indexedDB 思考

前端存储极大地提升了,页面资源加载的速度,但是由于前端存储是存储在客户端上,所以我们并不能确定数据的安全性与完整性,所以很多时候我们并不能相信前端存储的信息。甚至有时因为清理缓存,导致数据丢失。这一点,我们要深入思考,防止数据的丢失。

总结下本文几个核心观点:

  • Cookie 的本职工作并非本地存储,而是“维持状态”;
  • Web Storage 是 HTML5 专门为浏览器存储而提供的数据存储机制,不与服务端发生通信;
  • IndexedDB 用于客户端存储大量结构化数据。
  • 正是浏览器存储、缓存技术的出现和发展,为我们的前端应用带来了无限的转机。近年来基于存储、缓存技术的第三方库层出不绝,此外还衍生出了 PWA 这样优秀的 Web 应用模型。未来应充分利用前端存储的优势来提升前端的使用体验。
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值