离线应用与客户端存储

离线检测

  • HTML5定义了navigator.onLine属性来检测设备是在线还是离线。这个属性为true表示设备能上网,值为false表示设备离线。这个属性的关键是浏览器必须知道设备能否访问网络,从而返回正确的值
  • 不同浏览器之间有小差异

    • IE6+和Safari5+能够正确检测到网络已经断开,并将navigator.onLine的值转换为false
    • Firefox3+和Opera10.6+支持navigator.onLine属性,但必须手工选中菜单项脱机工作才能让浏览器正常工作
    • Chrome11以及之前版本始终将navigator.onLine属性设置为true
  • 为了更好确定网络是否可用,HTML5还定义了两个事件onLine和offLine。当网络从离线变为在线或者从在线变为离线时,分别触发这两个事件

    EventUtil.addHandler(window, "online", function(){alert("Online");
    });
    EventUtil.addHandler(window, "offline", function(){alert("Offline");
    });

应用缓存

  • applicationCache 对象,status属性,属性的值是常量,表示应用缓存的如下当前状态。

    • 0,无缓存,即没有与页面相关的应用缓存。
    • 1,闲置,即应用缓存未得到更新。
    • 2,检查中,即正在下载描述文件并检查更新。
    • 3,下载中,即应用缓存正在下载描述文件中指定的资源。
    • 4,更新完成,即应用缓存已经更新了资源,而且所有资源都已下载完毕,可以通过 swapCache()来使用了。
    • 5,废弃,即应用缓存的描述文件已经不存在了,因此页面无法再访问应用缓存。
  • 应用缓存还有很多相关的事件,表示其状态的改变。以下是这些事件。

    • checking,在浏览器为应用缓存查找更新时触发。
    • error,在检查更新或下载资源期间发生错误时触发。
    • noupdate,在检查描述文件发现文件无变化时触发。
    • downloading,在开始下载应用缓存资源时触发。
    • progress,在文件下载应用缓存的过程中持续不断地触发。
    • updateready,在页面新的应用缓存下载完毕且可以通过 swapCache() 使用时触发。
    • cached,在应用缓存完整可用时触发。

数据存储

Cookie

  • cookie在性质上是绑定在特定的域名下的。当设定了一个cookie后,再给创建它的域名发送请求时都会包含这个cookie。这个限制确保了储存在cookie中的信息只能让批准的接受者访问,而无法被其他域访问
  • cookie由浏览器保存的几部分组成

    • 名称,一个唯一确定 cookie 的名称。cookie 名称是不区分大小写的,所以 myCookie 和 MyCookie被认为是同一个 cookie。然而,实践中最好将 cookie 名称看作是区分大小写的,因为某些服务器会这样处理 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。
  • 基本的cookie操作有3种:读取、写入和删除

    //设置 cookie
    CookieUtil.set("name", "Nicholas");
    CookieUtil.set("book", "Professional JavaScript");
    //读取 cookie 的值
    alert(CookieUtil.get("name")); //"Nicholas"
    alert(CookieUtil.get("book")); //"Professional JavaScript"
    //删除 cookie
    CookieUtil.unset("name");
    CookieUtil.unset("book")
    //设置 cookie,包括它的路径、域、失效日期
    CookieUtil.set("name", "Nicholas", "/books/projs/", "www.wrox.com",new Date("January 1, 2010"));
    //删除刚刚设置的 cookie
    CookieUtil.unset("name", "/books/projs/", "www.wrox.com");
    //设置安全的 cookie
    CookieUtil.set("name", "Nicholas", null, null, null, true);
  • 确保删除cookie

    //设置 cookie
    CookieUtil.set("name", "Nicholas");
    CookieUtil.set("book", "Professional JavaScript");
    //读取 cookie 的值
    alert(CookieUtil.get("name")); //"Nicholas"
    alert(CookieUtil.get("book")); //"Professional JavaScript"
    //删除 cookie
    CookieUtil.unset("name");
    CookieUtil.unset("book")
    //设置 cookie,包括它的路径、域、失效日期
    CookieUtil.set("name", "Nicholas", "/books/projs/", "www.wrox.com",new Date("January 1, 2010"));
    //删除刚刚设置的 cookie
    CookieUtil.unset("name", "/books/projs/", "www.wrox.com");
    //设置安全的 cookie
    CookieUtil.set("name", "Nicholas", null, null, null, true);
  • 子cookie一般以查询字符串的格式进行格式化。然后这些值可以使用单个cookie进行存储和访问,而非对每个名称-值对儿使用不同的cookie存储
  • 要获得一个子cookie,首先要遵循与获得cookie一样的基本步骤,但是在解码cookie值之前,需要按下面方法找出子cookie信息

    var SubCookieUtil = {get: function (name, subName){var subCookies = this.getAll(name);if (subCookies){return subCookies[subName];} else {return null;}},getAll: function(name){var cookieName = encodeURIComponent(name) + "=",cookieStart = document.cookie.indexOf(cookieName),cookieValue = null,cookieEnd,subCookies,i,parts,result = {};if (cookieStart > -1){cookieEnd = document.cookie.indexOf(";", cookieStart);if (cookieEnd == -1){cookieEnd = document.cookie.length;}cookieValue = document.cookie.substring(cookieStart +cookieName.length, cookieEnd);if (cookieValue.length > 0){subCookies = cookieValue.split("&");for (i=0, len=subCookies.length; i < len; i++){parts = subCookies[i].split("=");result[decodeURIComponent(parts[0])] =decodeURIComponent(parts[1]);}return result;}}return null;},//省略了更多代码
    };
  • 要设置子cookie,也有两种方法set()和setAll()

    var SubCookieUtil = {set: function (name, subName, value, expires, path, domain, secure) {var subcookies = this.getAll(name) || {};subcookies[subName] = value;this.setAll(name, subcookies, expires, path, domain, secure);},setAll: function(name, subcookies, expires, path, domain, secure){var cookieText = encodeURIComponent(name) + "=",subcookieParts = new Array(),subName;for (subName in subcookies){if (subName.length > 0 && subcookies.hasOwnProperty(subName)){subcookieParts.push(encodeURIComponent(subName) + "=" +encodeURIComponent(subcookies[subName]));}}if (cookieParts.length > 0){cookieText += subcookieParts.join("&");if (expires instanceof Date) {cookieText += "; expires=" + expires.toGMTString();}if (path) {cookieText += "; path=" + path;}if (domain) {cookieText += "; domain=" + domain;}if (secure) {cookieText += "; secure";}} else {cookieText += "; expires=" + (new Date(0)).toGMTString();}document.cookie = cookieText;},//省略了更多代码
    };
  • 为了删除一个子cookie,首先必须获得包含在某个cookie中的所有子cookie,然后仅删除需要删除的那个子cookie,然后再将余下的子cookie的值保存为cookie的值

    var SubCookieUtil = {//这里省略了更多代码unset: function (name, subName, path, domain, secure){var subcookies = this.getAll(name);if (subcookies){delete subcookies[subName];this.setAll(name, subcookies, null, path, domain, secure);}},unsetAll: function(name, path, domain, secure){this.setAll(name, null, new Date(0), path, domain, secure);}
    };

IE用户数据

  • 一旦元素使用了userData行为,那么就可以使用setAttribute()方法在上面保存数据了,为了将数据提交到浏览器缓存中,还必须调用save()方法,并告诉它要保存的数据空间的名字

    var dataStore = document.getElementById("dataStore");
    dataStore.setAttribute("name", "Nicholas");
    dataStore.setAttribute("book", "Professional JavaScript");
    dataStore.save("BookInfo")

Web存储机制

Storage类型

  • Storage的实例与其他对象类似

    • clear(),删除所有值,Firefox中没有实现
    • getItem(name),根据指定的名字name获取对应的值
    • key(index),获得index位置处的值的名字
    • removeItem(name),删除由name指定的名值对儿
    • setItem(name,value),为指定的name设置一个对应的值

sessionStorage对象

  • 可以使用setItem()或者直接设置新的属性类存储数据

    //使用方法存储数据
    sessionStorage.setItem("name", "Nicholas");
    //使用属性存储数据
    sessionStorage.book = "Professional JavaScript";
  • sessionStroage中有数据时,可以使用getItem()或者通过直接访问属性名来获取数据

    //使用方法读取数据
    var name = sessionStorage.getItem("name");
    //使用属性读取数据
    var book = sessionStorage.book;

globalStorage对象

  • 可以通过方括号标记使用属性来实现

    //保存数据
    globalStorage["wrox.com"].name = "Nicholas";
    //获取数据
    var name = globalStorage["wrox.com"].name;

localStorage对象

  • 可以像使用sessionStorage一样来使用

    //使用方法存储数据
    localStorage.setItem("name", "Nicholas");
    //使用属性存储数据
    localStorage.book = "Professional JavaScript";
    //使用方法读取数据
    var name = localStorage.getItem("name");
    //使用属性读取数据
    var book = localStorage.book;

storage事件

  • 对Storage对象进行任何修改,都会在文档上触发storage事件,当通过属性或setItem()方法保存数据,使用delete操作符或removeItem()删除数据,或者调用clear()方法时,都会发生这个事件,这个事件的event对象有以下属性

    • domain,发生变化的存储空间的域名
    • key,设置或者删除的键名
    • newValue,如果是设置值,则是新值,如果嘶吼删除键,则是null
    • oldValue,键被更改之前的值

IndexedDB

数据库

  • IndexedDB最大的特色就是使用对象保存数据,而不是使用表来保存数据
  • 使用IndexedDB第一步是打开它,将要打开的数据库名传给indexedDB.open()。如果传入的数据库已经存在,就会发送一个打开它的请求,如果传入的数据库不存在,就发送一个创建并打开它的请求

    var request, database;
    request = indexedDB.open("admin");
    request.onerror = function(event){alert("Something bad happened while trying to open: " +event.target.errorCode);
    };
    request.onsuccess = function(event){database = event.target.result;
    }
  • 可能的错误码

    • IDBDatabaseException.UNKNOWN_ERR (1),意外错误,无法归类。
    • IDBDatabaseException.NON_TRANSIENT_ERR (2),操作不合法。
    • IDBDatabaseException.NOT_FOUND_ERR (3),未发现要操作的数据库。
    • IDBDatabaseException.CONSTRAINT_ERR (4),违反了数据库约束。
    • IDBDatabaseException.DATA_ERR (5),提供给事务的数据不能满足要求。
    • IDBDatabaseException.NOT_ALLOWED_ERR (6),操作不合法。
    • IDBDatabaseException.TRANSACTION_INACTIVE_ERR (7),试图重用已完成的事务。
    • IDBDatabaseException.ABORT_ERR (8),请求中断,未成功。
    • IDBDatabaseException.READ_ONLY_ERR (9),试图在只读模式下写入或修改数据。
    • IDBDatabaseException.TIMEOUT_ERR (10),在有效时间内未完成操作。
    • IDBDatabaseException.QUOTA_ERR (11),磁盘空间不足
  • IndexedDB没有版本号,一开始为数据库指定一个版本号,可以调用setVersion()方法,传入以字符串形式表示的版本号

    if (database.version != "1.0"){request = database.setVersion("1.0");request.onerror = function(event){alert("Something bad happened while trying to set version: " +event.target.errorCode);};request.onsuccess = function(event){alert("Database initialization complete. Database name: " + database.name +", Version: " + database.version);};
    } else {alert("Database already initialized. Database name: " + database.name +", Version: " + database.version);
    }

对象存储空间

  • 如果想验证请求是否成功完成,可以把返回的请求对象保存在一个变量中,然后再指定onerror或onsuccess事件处理程序

    //users 中保存着一批用户对象
    var i=0,request,requests = [],len = users.length;
    while(i < len){request = store.add(users[i++]);request.onerror = function(){// 处理错误};request.onsuccess = function(){// 处理成功};requests.push(request);
    }

事务

  • 事务对象本身也有事件处理程序:onerror和oncomplete

    transaction.onerror = function(event){//整个事务都被取消了
    };
    transaction.oncomplete = function(event){//整个事务都成功完成了
    };

使用游标查询

  • 实例有几个属性

    • direction,数值,表示游标移动的方向。默认值为 IDBCursor.NEXT (0),表示下一项。IDBCursor.NEXT_NO_DUPLICATE (1)表示下一个不重复的项, IDBCursor.PREV (2)表示前一项,而 IDBCursor.PREV_NO_DUPLICATE 表示前一个不重复的项。
    • key,对象的键。
    • value,实际的对象。
    • primaryKey,游标使用的键。可能是对象键,也可能是索引键
  • 调用update()方法可以指定的对象更新当前游标的value,与其他操作一样,调用update()方法也会创建一个新的请求

    request.onsuccess = function(event){var cursor = event.target.result,value,updateRequest;if (cursor){ //必须要检查if (cursor.key == "foo"){value = cursor.value; // 取得当前的值value.password = "magic!"; // 更新密码updateRequest = cursor.update(value); // 请求保存更新updateRequest.onsuccess = function(){// 处理成功};updateReqeust.onerror = function(){// 处理失败};}}
    };
  • 调用delete()方法,会删除相应的记录

    request.onsuccess = function(event){var cursor = event.target.result,value,deleteRequest;if (cursor){ //必须要检查if (cursor.key == "foo"){deleteRequest = cursor.delete(); // 请求删除当前项deleteRequest.onsuccess = function(){// 处理成功};deleteRequest.onerror = function(){// 处理失败};}}
    };
  • 默认情况下,每个游标只发起一次请求,要想发起另一次请求,必须调用下面的方法

    • continue(key),移动到结果集中的下一项。参数 key 是可选的,不指定这个参数,游标移动到下一项;指定这个参数,游标会移动到指定键的位置。
    • advance(count),向前移动 count 指定的项数。

键范围

  • 先声明一个本地的类型

    var IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange;
  • 四种方式

    • 使用only()方法

      var onlyRange = IDBKeyRange.only("007");
    • 指定结果集的下界

      //从键为"007"的对象开始,然后可以移动到最后
      var lowerRange = IDBKeyRange.lowerBound("007");
      //如果你想忽略键为 "007" 的对象,从它的下一个对象开始,那么可以传入第二个参数 true
      //从键为"007"的对象的下一个对象开始,然后可以移动到最后
      var lowerRange = IDBKeyRange.lowerBound("007", true);
    • 指定结果集的上界

      //从头开始,到键为"ace"的对象为止
      var upperRange = IDBKeyRange.upperBound("ace");
      //如果你不想包含键为指定值的对象,同样,传入第二个参数 true
      //从头开始,到键为"ace"的对象的上一个对象为止
      var upperRange = IDBKeyRange.upperBound("ace", true);
    • 同时指定结果集的上界下界。这个方法接收4个参数:表示下界的键、表示上界的键、可选的表示是否跳过下界的布尔值、可选的表示是否跳过上界的布尔值

      //从键为"007"的对象开始,到键为"ace"的对象为止
      var boundRange = IDBKeyRange.bound("007", "ace");
      //从键为"007"的对象的下一个对象开始,到键为"ace"的对象为止
      var boundRange = IDBKeyRange.bound("007", "ace", true);
      //从键为"007"的对象的下一个对象开始,到键为"ace"的对象的上一个对象为止
      var boundRange = IDBKeyRange.bound("007", "ace", true, true);
      //从键为"007"的对象开始,到键为"ace"的对象的上一个对象为止
      var boundRange = IDBKeyRange.bound("007", "ace", false, true)

索引

  • 要创建索引,首先引用对象存储空间,然后调用createIndex()方法

    var store = db.transaction("users").objectStore("users"),index = store.createIndex("username", "username", { unique:  false });
  • 索引上调用openCursor()方法可以创建新的游标,除了将来会把索引键而非主键保存在event.result.key属性中之外,这个游标与在对象存储空间上调用openCursor()返回的游标完全一样

    var store = db.transaction("users").objectStore("users"),index = store.index("username"),request = index.openCursor();
    request.onsuccess = function(event){//处理成功
    };
  • 索引上也能创建一个特殊的只返回每条记录主键的游标,调用openKeyCursor()方法,这个方法接收的参数与openCursor()相同

    var store = db.transaction("users").objectStore("users"),index = store.index("username"),request = index.openKeyCursor();
    request.onsuccess = function(event){//处理成功// event.result.key 中保存索引键,而 event.result.value 中保存主键
    };
  • 使用get()方法能够从索引中取得一个对象

    var store = db.transaction("users").objectStore("users"),index = store.index("username"),request = index.get("007");
    request.onsuccess = function(event){//处理成功
    };
    request.onerror = function(event){//处理失败
    };
  • 要根据给定的索引键取得主键,可以使用getKey()方法

    var store = db.transaction("users").objectStore("users"),index = store.index("username"),request = index.getKey("007");
    request.onsuccess = function(event){//处理成功//event.result.key 中保存索引键,而 event.result.value 中保存主键
    }
  • 通过IDBIndex对象的属性可以获得有关索引的相关信息

    • name,索引的名字
    • keyPath,传入createIndex()中的属性路径
    • objectStore,索引的对象存储空间
    • unique,表示索引键是否唯一的布尔值
  • 下面代码可以知道根据存储的对象建立了哪些索引

    var store = db.transaction("users").objectStore("users"),indexNames = store.indexNames,index,i = 0,len = indexNames.length;
    while(i < len){index = store.index(indexNames[i++]);console.log("Index name: " + index.name + ", KeyPath: " + index.keyPath +", Unique: " + index.unique);
    }

并发问题

  • 刚打开数据库时,要记着指定 onversionchange 事件处理程序。当同一个来源的另一个标签页调
    用 setVersion() 时,就会执行这个回调函数。处理这个事件的最佳方式是立即关闭数据库,从而保证
    版本更新顺利完成

    var request, database;
    request = indexedDB.open("admin");
    request.onsuccess = function(event){database = event.target.result;database.onversionchange = function(){database.close();};
    }

限制

  • IndexedDB数据库只能由同源页面操作,因此不能跨域共享信息
  • 每个来源的数据库占用的磁盘空间有限制
  • 不允许本地文件访问IndexedDB

高程3总结#第23章离线应用与客户端存储相关推荐

  1. html5 将资源存于客户端,HTML5离线应用与客户端存储的实现

    html5几种在客户端存储数据的实例详解 LocalStorage LocalStorage用于持久化的本地存储,存储资料在客户端(client)的浏览器上,除非主动删除数据,否则数 据是永远不会过期 ...

  2. HTML5(3) -- 离线缓存与客户端存储总结

    1.离线检测 可以使用navigator.onLine属性来检测 <!DOCTYPE html> <html lang="en"><head>& ...

  3. 信安教程第二版-第23章云计算安全需求分析与安全保护工程

    第23章 云计算安全需求分析与安全保护工程 23.1 云计算安全概念与威胁分析 503 23.1.1 云计算基本概念 503 23.1.2 云计算安全分析 504 23.1.3 云计算安全要求 508 ...

  4. 第 23 章 解释器模式

    第 23 章 解释器模式 1.四则运算问题 通过解释器模式来实现四则运算,如计算a+b-c的值,具体要求 先输入表达式的形式,比如 a+b+c-d+e,要求表达式的字母不能重复 在分别输入 a, b, ...

  5. 《MATLAB智能算法30个案例》:第23章 基于蚁群算法的二维路径规划算法

    <MATLAB智能算法30个案例>:第23章 基于蚁群算法的二维路径规划算法 1. 前言 2. MATLAB 仿真示例 3. 小结 1. 前言 <MATLAB智能算法30个案例分析& ...

  6. 【第23章】云计算安全需求分析与安全保护工程(软考:信息安全工程师)--学习笔记

    第23章 云计算安全需求分析与安全保护工程 23.1 云计算安全概念与威胁分析 23.1.1 云计算基本概念 在传统计算环境下,用户构建一个新的应用系统,需要做大量繁杂的工作,如采购硬件设备.安装软件 ...

  7. 系统集成项目管理工程师:第23章案例分析学习笔记

    第23章案例分析 一.目录 23.1 整体管理案例  23.1.1 整体管理案例一 23.1.2 整体管理案例二 23.1.3 整体管理案例三 23.2 范围管理案例  23.2.1 范围管理案例一 ...

  8. JavaSE 进阶 - 第23章 IO流

    JavaSE 进阶 - 第23章 IO流 1.IO流,什么是IO? 2.IO流的分类 3.流应该怎样学习? 4.java IO流的四大家族 5.java.io包下需要掌握的16个流 5.1 FileI ...

  9. 自然语言处理NLP星空智能对话机器人系列 第23章 MRC经典的Span Extraction模型Bi-DAF 算法

    第23章:MRC经典的Span Extraction模型Bi-DAF 算法架构.运行机制及数学原理 1,双向Attention Flow:Query2Context.Context2Query数学原理 ...

  10. JavaSE——第23章 反射reflection

    JavaSE--第23章 反射reflection(老师:韩顺平) 文章目录 JavaSE--第23章 反射reflection(老师:韩顺平) 23.2 反射机制 23.2.1 Java refle ...

最新文章

  1. BQ27510 电量计的校准 的 C语言实现
  2. mysql创建非聚集索引_一文看懂聚集索引和非聚集索引的区别
  3. things to be done
  4. python不定长参数详解
  5. 转载 :配置ssh密钥认证自动登录
  6. python实现括号分组
  7. HDU 5970 CCPC2016合肥 求等差数列整除整数下取整求和
  8. 云呐|固定资产盘点管理办法
  9. unity3d C#UnityEngine API 提示中文汉化
  10. 使用中控指纹采集器开发指纹识别案例V1.0
  11. python根据词性进行词频统计_如何根据词性来确定语篇中的词频?
  12. Centos里tftp服务器的安装和配置
  13. 纪念数学家胡世华先生逝世20周年
  14. Python经典例题:跑马灯文字效应
  15. Android 图片框架原理——Glide源码分析
  16. 脱敏数据的残余风险评估
  17. Excel中Chart对象成员表
  18. GSM的调制方式-GMSK
  19. 【Person Re-ID】AlignedReID: Surpassing Human-Level Performance in Person Re-Identification
  20. Excel 设置下拉框-显示中文而实际数字

热门文章

  1. htmlmo标签,HTML5 MathML
  2. ai第二次热潮:思维的转变_基于属性的建议:科技创业公司如何使用AI来转变在线评论和建议
  3. 动态瑜伽 静态瑜伽 初学者_使用计算机视觉对瑜伽姿势进行评分
  4. 重庆市计算机一级考试2015,2015年计算机一级考试试题及答案
  5. mysql 组复制详解_MySQL 5.7: 使用组复制(MySQL Group Replication)
  6. 会计云课堂实名认证后怎么更改_会计云课堂网上听课步骤详解
  7. Hive 实用的第三方 UDF 收集
  8. RabbitMQ 集群原理和完善
  9. spring异常:Could not resolve placeholder
  10. Hyper-V 2016 系列教程56 SCVMM 2016 Client的安装