1 序

离线应用与客户端存储 - 图1

2 离线检测

navigato.onLine属性检测设施是否离线。

HTML5还定义了两个事件online和offline。当网络从离线变为在线或者从在线变为离线时,分别触发这两个事件。
离线应用与客户端存储 - 图2
离线应用与客户端存储 - 图3

3 应用缓冲

AppCache已废弃
该特性已经从 Web 标准中删除,虽然一些浏览器目前仍然支持它,但也许会在未来的某个时间停止支持,请尽量不要使用该特性。
在此刻使用这里描述的应用程序缓存功能高度不鼓励; 它正在处于从Web平台中被删除的过程。请改用Service Workers 代替。事实上,在Firefox 44中,当AppCache用于为页面提供离线支持时,控制台中现在显示一条警告消息,建议开发人员改用 Service workers 代替 (bug 1204581)。

4 数据存储

5 Cookie

6 限制

数量限制:cookie单个域早期最多20个,限制最多50个。超出会删除之前设置的cookie。

大小限制:一个域下所有的cookie大小不能超过4095b。超出尺寸限制,cookie会被悄无声息的丢掉。

7 cookie构成

  • 名称:cookie名称不区分大小写,必须经过URL编码。
  • 值:储存在cookie中的字符串值。值必须被URL编码。
  • 域:cookie对于哪个域是有效的。所有向该域发送的请求中都会包含这个cookie信息。这个值可以包含子域(subdomain,如www.wrox.com),也可以不包含它(如.wrox.com,则对于wrox.com的所有子域都有效)。如果没有明确设定,那么这个域会被认作来自设置cookie的那个域。
  • 路径:对于指定域中的那个路径,应该向服务器发送cookie。例如,你可以指定cookie只有从http://www.wrox.com/books/中才能访问,那么http://www.wrox.com的页面就不会发送cookie信息,即使请求都是来自同一个域的。
  • 失效时间:表示cookie何时应该被删除的时间戳(也就是,何时应该停止向服务器发送这个cookie)。默认情况下,浏览器会话结束时即将所有cookie删除;不过也可以自己设置删除时间。这个值是个GMT格式的日期(Wdy,DD-Mon-YYYY HH:MM:SS GMT),用于指定应该删除cookie的准确时间。因此,cookie可在浏览器关闭后依然保存在用户的机器上。如果你设置的失效日期是以前的时间,则cookie会被立刻删除。
  • 安全标志:指定后,cookie只有在使用SSL连接的时候才发送到服务器。例如,cookie信息只能发送给https://www.wrox.com,而http://www.wrox.com的请求则不能发送cookie。

离线应用与客户端存储 - 图4

离线应用与客户端存储 - 图5

8 JavaScript中的cookie

doucument.cookie属性设置和获取cookie的值;如果操作可以使用JavaScript书中封装的cookie方法。
在JavaScript中处理cookie有些复杂,因为其众所周知的蹩脚的接口,即BOM的document.cookie属性。这个属性的独特之处在于它会因为使用它的方式不同而表现出不同的行为。当用来获取属性值时,document.cookie返回当前页面可以的(根据cookie的域、路径、失效时间和安全设置)所有cookie的字符串,一系列由分号隔开的名值对儿,如下列所示:
离线应用与客户端存储 - 图6

当用户设置值的时候,document.cookie属性可以设置为一个新的cookie字符串。这个cookie字符串会被解释并添加到现有的cookie集合中。设置document.cookie并不会覆盖cookie,除非设置的cookie名称已经存在。
设置cookie的格式如下:(和set-cookie头中使用的格式一样。)离线应用与客户端存储 - 图7

9 cookie和session的区别

a、session是一种服务器端的状态管理技术。b、session是基于cookie的技术。c、当浏览器访问服务器时,服务器会创建一个session对象(该对象有一个唯一的id号,称之为sessionId)服务器在默认的情况下,会将sessionId以cookie的方式,发送给浏览器,浏览器会将sessionId保存到内存中。当浏览器再次访问服务器时,会将sessionId发送给服务器,服务器依据sessionId就可以找到之间创建的session对象。

10 Web存储机制

Web Storage的两个主要目标是:

  • 提供一种在cookie之外存储会话数据的途径;
  • 提供一种存储大量可以跨会话存在的数据的机制。

离线应用与客户端存储 - 图8

11 Storage

离线应用与客户端存储 - 图9
离线应用与客户端存储 - 图10
离线应用与客户端存储 - 图11

12 sessionStorage

离线应用与客户端存储 - 图12
离线应用与客户端存储 - 图13

13 globalStorage

被废弃了。被localStorage替代。

14 localStorage

localStorage对象在修订过的HTML5规范中作为持久保存客户端数据的方案取代了globalStorage。与globalStorage不同,不能给localStorage指定任何访问规则;规则事先就设定好了。要访问同一个localStorage对象,页面必须来自同一个域名(子域名无效),使用同一种协议,在同一个端口上。这相当于globalStorage[location.host]。
由于localStorage是Storage的实例,所以key像使用sessionStorage一样使用它。下面是一些例子:
离线应用与客户端存储 - 图14

15 Storage事件

离线应用与客户端存储 - 图15
MDN介绍Storage:https://developer.mozilla.org/en-US/docs/Web/API/StorageEvent
无法触发storage事件请参考实例:https://blog.csdn.net/ixygj197875/article/details/80114096

16 限制

离线应用与客户端存储 - 图16

17 IndexedDB

更多资料:https://www.imooc.com/article/17744
实例代码:https://github.com/lin-xin/blog/tree/master/indexedDB-demo

18 数据库

  1. IndexedDB 是一个用于在浏览器中储存较大数据结构的 Web API, 并提供索引功能以实现高性能查找. 像其他基于 SQL 关系型数据库管理系统 (RDBMS) 一样, IndexedDB 是一个事务型的数据库系统. 然而, 它是使用 JavaScript 对象而非列数固定的表格来储存数据的.<br />indexedDB.open()打开和创建数据库<br /> 使用IndexedDB的第一步是打开它,即把要打开的数据库名传给indexDB.open()。如果传入的数据库已经存在,就会发送一个打开它的请求;如果传入的数据库还不存在,就会发送一个创建并打开它的请求。总之,调用indexedDB.open()会返回一个IDBRequest对象,在这个对象上可以添加onerroronsuccess事件处理程序。例子:<br />![](https://cdn.nlark.com/yuque/0/2021/png/291746/1636093927549-2435afad-bcd1-4b9e-a2db-b1af9658531e.png#)

19 对象存储空间

20 事务

21 游标查询

22 键范围

23 设定游标方向

24 索引

25 并发问题

26 限制

更多细节请查看《JavaScript高级程序设计3版》书中内容。

27 IndexedDB操作流程

参考文章:http://www.ruanyifeng.com/blog/2018/07/indexeddb.html
Indexed Database API,或者简称为IndexedDB,是在浏览器中保存结构化数据的一种数据库。IndexedDB是为了替代目前已被废弃的Web SQL Database API而出现的。IndexedDB的思想是创建一套API,方便保存和读取JavaScript对象,同时还支持查询及搜索。
IndexedDB设计的操作完全是异步进行的。因此,大多数操作会以请求方式进行,但这些操作会在后期执行,然后如果成功则返回结果,如果失败则返回错误。差不多每一次IndexedDB操作,都需要你注册onerror或onsuccess事件处理程序,以确保适当地处理结果。
IndexedDB兼容模式:
离线应用与客户端存储 - 图17

28 打开数据库

使用 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(‘数据库打开报错’);
};

  1. success 事件

success事件表示成功打开数据库。
var db;
request.onsuccess = function (event) {
db = request.result;
console.log(‘数据库打开成功’);
};
这时,通过request对象的result属性拿到数据库对象。

  1. upgradeneeded 事件

如果指定的版本号,大于数据库的实际版本号,就会发生数据库升级事件upgradeneeded。
var db;
request.onupgradeneeded = function (event) {
db = event.target.result;
}
这时通过事件对象的target.result属性,拿到数据库实例。

29 新建数据库

新建数据库与打开数据库是同一个操作。如果指定的数据库不存在,就会新建。不同之处在于,后续的操作主要在upgradeneeded事件的监听函数里面完成,因为这时版本从无到有,所以会触发这个事件。
通常,新建数据库以后,第一件事是新建对象仓库(即新建表)。
request.onupgradeneeded = function(event) {
db = event.target.result;
var objectStore = db.createObjectStore(‘person’, { keyPath: ‘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 自动生成主键。

  1. var objectStore = db.createObjectStore(<br /> 'person',<br /> { autoIncrement: true }<br /> );<br />上面代码中,指定主键为一个递增的整数。<br />新建对象仓库以后,下一步可以新建索引。<br />request.onupgradeneeded = function(event) {<br /> db = event.target.result;<br /> var objectStore = db.createObjectStore('person', { keyPath: 'id' });<br /> objectStore.createIndex('name', 'name', { unique: false });<br /> objectStore.createIndex('email', 'email', { unique: true });<br />}<br />上面代码中,IDBObject.createIndex()的三个参数分别为索引名称、索引所在的属性、配置对象(说明该属性是否包含重复的值)。

30 新增数据

新增数据指的是向对象仓库写入数据记录。这需要通过事务完成。

  1. function add() {<br /> var request = db.transaction(['person'], 'readwrite')<br /> .objectStore('person')<br /> .add({ id: 1, name: '张三', age: 24, email: 'zhangsan@example.com' });
  2. request.onsuccess = function (event) {<br /> console.log('数据写入成功');<br /> };
  3. request.onerror = function (event) {<br /> console.log('数据写入失败');<br /> }<br /> }
  4. add();<br />上面代码中,写入数据需要新建一个事务。新建时必须指定表格名称和操作模式("只读""读写")。新建事务以后,通过IDBTransaction.objectStore(name)方法,拿到 IDBObjectStore 对象,再通过表格对象的add()方法,向表格写入一条记录。<br />写入操作是一个异步操作,通过监听连接对象的success事件和error事件,了解是否写入成功。

31 读取数据

读取数据也是通过事务完成。

  1. function read() {<br /> var transaction = db.transaction(['person']);<br /> var objectStore = transaction.objectStore('person');<br /> var request = objectStore.get(1);
  2. request.onerror = function(event) {<br /> console.log('事务失败');<br /> };
  3. request.onsuccess = function( event) {<br /> if (request.result) {<br /> console.log('Name: ' + request.result.name);<br /> console.log('Age: ' + request.result.age);<br /> console.log('Email: ' + request.result.email);<br /> } else {<br /> console.log('未获得数据记录');<br /> }<br /> };<br /> }
  4. read();<br />上面代码中,objectStore.get()方法用于读取数据,参数是主键的值。

32 遍历数据

遍历数据表格的所有记录,要使用指针对象 IDBCursor。

  1. function readAll() {<br /> var objectStore = db.transaction('person').objectStore('person');
  2. objectStore.openCursor().onsuccess = function (event) {<br /> var cursor = event.target.result;
  3. if (cursor) {<br /> console.log('Id: ' + cursor.key);<br /> console.log('Name: ' + cursor.value.name);<br /> console.log('Age: ' + cursor.value.age);<br /> console.log('Email: ' + cursor.value.email);<br /> cursor.continue();<br /> } else {<br /> console.log('没有更多数据了!');<br /> }<br /> };<br /> }
  4. readAll();<br />上面代码中,新建指针对象的openCursor()方法是一个异步操作,所以要监听success事件。

33 更新数据

更新数据要使用IDBObject.put()方法。

  1. function update() {<br /> var request = db.transaction(['person'], 'readwrite')<br /> .objectStore('person')<br /> .put({ id: 1, name: '李四', age: 35, email: 'lisi@example.com' });
  2. request.onsuccess = function (event) {<br /> console.log('数据更新成功');<br /> };
  3. request.onerror = function (event) {<br /> console.log('数据更新失败');<br /> }<br /> }
  4. update();<br />上面代码中,put()方法自动更新了主键为1的记录。

34 删除数据

IDBObjectStore.delete()方法用于删除记录。

  1. function remove() {<br /> var request = db.transaction(['person'], 'readwrite')<br /> .objectStore('person')<br /> .delete(1);
  2. request.onsuccess = function (event) {<br /> console.log('数据删除成功');<br /> };<br /> }
  3. remove();

35 使用索引

索引的意义在于,可以让你搜索任意字段,也就是说从任意字段拿到数据记录。如果不建立索引,默认只能搜索主键(即从主键取值)。
假定新建表格的时候,对name字段建立了索引。
objectStore.createIndex(‘name’, ‘name’, { unique: false });
现在,就可以从name找到对应的数据记录了。

  1. var transaction = db.transaction(['person'], 'readonly');<br /> var store = transaction.objectStore('person');<br /> var index = store.index('name');<br /> var request = index.get('李四');
  2. request.onsuccess = function (e) {<br /> var result = e.target.result;<br /> if (result) {<br /> // ...<br /> } else {<br /> // ...<br /> }<br /> }