资讯专栏INFORMATION COLUMN

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

MobService / 789人阅读

摘要:离线应用与客户端存储离线检测定义了属性来检测设备是在线还是离线。应用缓存还有很多相关的事件,表示其状态的改变。

离线应用与客户端存储 离线检测

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/98195.html

相关文章

  • cookie简介

    摘要:简介几种常用属性属性值是类型,表示是否禁止客户端,也就是网页中使用操作默认在浏览器中的可以通过查看对应每一行后面的一栏是否被勾选来判断,若被勾选,表示当前,不能通过获取。 Cookie 简介 几种常用属性 httpOnly: 属性值是boolean类型,表示是否禁止客户端,也就是网页中使用js 操作cookie,默认false,在浏览器中的可以 通过查看对应每一行cookie后面的H...

    luxixing 评论0 收藏0
  • JS学习笔记(23章)(离线应用户端存储1)

    摘要:应用缓存的应用缓存,或者简称为,是专门为开发离线应用而设计的。应用缓存还有很多相关的事件,表示其状态的改变。数据存储,通常直接叫做,最初是在客户端用于存储会话信息的。也就是使用值来存储多个名称值对儿。 所谓Web离线应用,就是在设备不能上网的情况下仍然可以运行的应用。开发离线Web应用需要几个步骤:(1)确保应用知道设备是否能上网;(2)应用还必须能访问一定的资源(图像、JavaScr...

    fjcgreat 评论0 收藏0
  • javascript-离线应用户端存储

    摘要:离线检测含义设备能否上网代码注和,和最新的没问题应用缓存缓存的目的是专门为网页离线设计的,当然在在线情况也会缓存机制当用户在地址输入请求的地址去请求网页时,浏览器会先本地缓存中查看是否有对应的缓存文件,如果有然后查看新鲜度就是是否过期了,如 23.1 离线检测 含义:设备能否上网 代码: navigator.onLine 注:IE6+和safari+5,firefox3+和ope...

    gecko23 评论0 收藏0
  • 高程3总结#3章基本概念

    摘要:基本概念语法区分大小写,中的一切变量函数名和操作符都区分大小写。要将一个值转换成对应的值,可以调用类型包括整数和浮点数值,基本数值字面量格式是十进制整数,除了十进制外还有八进制十六进制。八进制第一位必须是,十六进制第一位必须是。 基本概念 语法 区分大小写,ECMAScript中的一切(变量、函数名和操作符)都区分大小写。函数名不能使用typeof,因为它是一个关键字,但typeOf...

    Rindia 评论0 收藏0

发表评论

0条评论

MobService

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<