客户端本地存储解决方案

无论是为了离线web应用,还是为了更好的用户体验,还是为了节省更多流量,很多web应用都需要我们能够在本地存储一些数据,于是出现了很多的基于浏览器的本地存储解决方案,比如cookie, userData, Flash SharedObject, Google Gears, WebStorage, Silverlight, Open Database等。

HTML5本地存储出来之前,本地存储方案有以下几种:
1. Cookie
由NetScape公司创造,指保存在用户本地终端内存或磁盘上的一小块数据,只能保存字符串类型,所有的cookie信息都会随着浏览器的请求而发送。cookie分为:
持久cookie: 数据保存在磁盘中。

会话cookie:数据保存在内存中,浏览器关闭后将被清除。

第一方cookie:当前访问站点的cookie。

第三方cookie: 非当前访问站点的cookie。假如当前正访问A网页,A网页中有访问B网页的链接,点击该链接,HTTP请求和响应头会包含B网页设置的cookie,相对于A网页即为第三方cookie。

尽管几乎所有浏览器都支持cookie,但容量小是它最致命的缺点,单域cookie数量不应超过20个,总长度不应超过4K,对于现在web应用本地存储数据量的需求是远远不够的,而且由于它是明文传输,禁止在cookie中保存如电话号码,账号密码等敏感信息。

2. UserData
由Microsoft公司在IE5.0中引入,指保存在用户本地终端的一块持久化字符串数据,除非手动删除或设置过期时间,否则数据将一直保存,只有IE5.0-IE9.0支持。它借助DHTML的behaviour属性来存储本地数据,允许每个页面最多存储64K数据,每个站点最多存储640K数据。它的缺点也是致命的,它并非Web标准,只有IE5.0-9.0支持,无法有效解决棘手的浏览器兼容问题。

3. Flash SharedObject
它允许你在本地客户端的硬盘或是服务器上存储所有flash支持的数据(Number, String, Array, Boolean, Object, XML等),数据会永久性保存,没有过期时间,可以通过设置管理器或调用clear()方法清除。按存放位置可以分为本地共享对象和远程共享对象。默认存储大小为100KB,用户可以手动设置,最大为10M。它的问题同样致命,作为flash,它拥有flash拥有的安全、稳定性低,耗系统资源等所有缺点。

4. Google Gears
Google于07年发布的一个开源浏览器插件,内置了一个基于SQLite的嵌入式SQL数据库,并提供了统一的API访问数据库。在取得用户授权之后,每个站点可以在数据库中存储大小不限的数据。但是Google早在chrome 12.0 中就已经放弃了对它的支持。

WebStorage
相对于上述本地存储方案,之后提出的HTML5本地存储中的WebStorage本地存储得到了最广泛的支持。浏览器兼容版本有IE8+/Chrome4+/FireFox3.5+/Safari4+/Opera10.5+。它包括localStorage和sessionStorage两种存储方式,均只保存字符串类型。Firefox3.5+/Opera10.5+/IE8+都支持最大存储5M的数据,而Chrome4+/Safari4+支持最大存储2.5M的数据。

localStorage用于持久化的本地存储,浏览器窗口关闭后,localStorage存储的数据仍然可以被访问。所有浏览器窗口可以共享localStorage的数据,保存的数据永远不会过期,只能手动删除。

而sessionStorage用于本地存储一个会话中的数据,它不是一种持久化的本地存储。这些数据只有在同一个会话中的页面才能访问,当前页面不可以访问新开页面的数据,并且会话结束后数据也随之销毁而无法使用。

localStorage和sessionStorage的属性和方法如下:


Name Description
length 获取存储的键值对的数量
remainingSpace 获取存储空间剩余空间的大小(非标准,仅IE8.0+支持)
setItem(key, value) 将value值存储到本地的key字段
removeItem(key) 删除指定key本地存储的值
getItem(key) 获取指定key本地存储的值
clear() 删除localStorage中存储的所有数据
key(index) 根据索引获取一个指定位置的键名

key-value对的形式不仅结构简单,而且各个平台都支持得非常好。下面三种方式都可以将字符串"hello"存储到本地的data字段。
  1. localStorage.setItem("data", "hello");
  2. localStorage.data = "hello";
  3. localStorage["data"] = "hello";
复制代码
同理,获取存储在本地的值也有上面三种方法。当然,还需要强调的是WebStorage保存的是字符串,如果数据是对象,得调用JSON对象的stringify()方法将它转换为字符串再保存。你可以像下面这样做一个简单的封装:
  1. var localData = {};
  2. localData.setValue = function(key, value){
  3.   if(value != undefined){
  4.     localStorage.setItem(key, JSON.stringify(value));
  5.   }else{
  6.     if(localStorage.getItem(key)){
  7.       return JSON.parse(localStorage.getItem(key));
  8.     }else{
  9.       return null;
  10.     }
  11.   }
  12. }
复制代码
如果你的程序需要在不同页面访问存储在localStorage中的同一个键值,你可能需要判断这个值是否已经被其他页面改变了,这可以通过向浏览器注册storage事件来实现,它包含许多有用的属性:

Ÿ   storageAera: 表示存储类型(session或者local)
Ÿ   key: 发生改变项的key

Ÿ   oldValue: key原来的值
Ÿ   newValue: key改变后的值
Ÿ   url:导致key发生改变的url

看下面简单的示例:

  1. window.addEventListener("storage", function(e){
  2.   console.log(e.key + "'s oldValue is" + e.oldValue);
  3.   console.log(e.key + "'s newValue is" + e.newValue);
  4. }, false);
  5. localStorage["name"] = "Mike";   //A页面
  6. localStorage["name"] = "Tom";   //B页面
复制代码
上面的代码会使你在A页面中看到:
name’s oldValue is Mike
name’s newValue is Tom

这里需要指出的是storage事件只在localStorage存储的某个键对应的值真正发生改变时才会触发,如果当前的存储区域本来就是空的,调用clear()去清空数据并不会触发该事件,或者你调用setItem()方法给某个键设置了与现有值相同的值,也不会触发该事件。

还有一点需要注意,在Firefox和Chrome中对storage事件的触发有点不同, 自身页面调用setItem()改变某键的值后并没有触发window的storage事件, 但是如果同时访问A.html和B.html, 在A页面中调用setItem()改变某键的值后能触发B页面中的storage事件, 反之同理。而在IE9中, 自身页面调用setItem()改变某键的值后也能触发window的storage事件。

WebStorage容量大、易用、原生支持等优点都使它成为首选的本地存储方案,当然它的安全性也较差,不能用它来保存敏感信息。

Indexed Database API
当然,HTML5的本地存储还有另外2个竞争者:Web SQL Database和Indexed Database。 虽然相比于Indexed Database,Web SQL Database得到了更多浏览器的支持,包括Chrome3+/Opera10.5+/Safari3.2+,但是它已经是一个被废弃了的规范,W3C官方在2011年11月声明将不再维护Web SQL Database规范,它大有被Indexed Database取代之势。所以,下面我们还是重点介绍下Indexed Database吧。

Indexed Database 是Oracle于2009年提出的,简称IndexedDB,是一种能让你在用户的浏览器中持久地存储结构化数据的数据库,为web应用提供了丰富的查询能力。它使用对象来保存数据,按域名分配独立空间,一个独立域名下可以创建多个数据库,每个数据库可以创建多个对象存储空间,一个对象存储空间相当于一个数据库表,可以存储多个对象数据。目前仅Chrome11+/Firefox4+/IE10支持。Firefox4+支持最大存储50MB的数据(移动端5MB),chrome11+支持最大存储5MB的数据。

IndexedDB的基本工作模式是:
1) 打开数据库并开始一个事务
2) 创建一个对象存储空间作为一种交互操作对象(object store)
3) 构建请求来执行数据库操作,如增加或查询数据
4) 通过监听DOM事件来等待操作完成
5) 最后处理“请求”结果(可以在request对象中找到)


我们一起来看一个简单的打开或创建一个数据库的例子:

  1. window.indexedDB=window.indexedDB||window.webkitIndexedDB||window.msIndexedDB||window.mozIndexedDB;   //浏览器兼容
  2. var request = indexedDB.open("MyTestDB");
  3. request.onsuccess = function(event){
  4.   var db = request.result;
  5. }
  6. request.onerror = function(event){
  7.   console.log("open error and errorCode is"+ request.errorCode );
  8. }
复制代码
上面代码首先调用IndexedDB对象唯一的方法open()方法打开名为”MyTestDB”的数据库,如果不存在,则会被创建。open()方法不会立刻打开数据库或者开始一个事务,它会返回一个我们可以作为事件来处理的包含result或者错误值的IDBOpenDBRequest对象。该对象有success和error事件属性。如果成功打开,会触发success事件,我们可以在事件处理程序里保存request.result这个IDBDatabase实例以供后面使用,如果发生错误,比如用户不允许你的web应用访问以创建一个数据库,则error事件会在request上触发。

然后,我们可以创建一个对象存储空间:
  1. var appData = [{appId:"9100", appName:"生活", appVersion:1},
  2. {appId:"8920", appName:"娱乐", appVersion:2}];
  3. request.onupgraddeneeded = function(event){
  4.   var db = event.target.result;
  5.   var objectStore = db.createObjectStore("Apps", {keyPath: "appId"});
  6.   objectSotre.createIndex("appName","appName", {unique:false});
  7.   for(var i in appData){
  8.     objectStore.add(appData[i]);
  9.   }
  10. }
复制代码
上述代码调用creatObjectStore()方法创建了一个名为Apps的对象存储空间,并且定义了一个使得存储空间中每个对象都是唯一的属性作为key path,这个属性就是appId。存储空间中的所有对象都必须拥有appId属性。此外,createIndex()方法创建了一个索引来通过appName查找app,可能会有重名的,所以不能使用unique索引。最后,调用存储空间对象的add()方法往里面添加数据。

接下来就是向我们之前创建的数据库中增加数据了。但是,在可以操作数据库之前,我们需要开始一个事务。事务来自于你创建的数据库对象,而且必须给它指定它需要跨越的对象存储空间。
  1. var transaction = db.transaction(["Apps"], "readwrite");
  2. transaction.oncomplete = function(event){
  3.   //当数据库添加数据完成时执行的操作
  4. };
  5. transaction.onerror = function(event){
  6.   //添加数据发生错误时的错误处理
  7. };
  8. var objectStore = transaction.objectStore("Apps");
  9. for(var i in appData){
  10.   var request = objectStore.add(appData[i]);
  11. }
  12. var request2 = objectStore.delete("9100");
  13. request2.onsuccess = function(event){
  14.   //删除数据成功后执行的操作
  15. };
复制代码
上面代码中的transaction()方法接收三个参数并返回一个事务对象。第一个参数是事务希望跨越的对象存储空间的列表,包含所有需要跨越的对象存储空间名,这里就是我们之前创建的对象存储空间Apps。后面两个参数可选,如果第二个参数未指定,默认表示得到只读事务。如果想进行写操作,用上面的"readwrite"指定。拥有了事务对象后,你就可以通过transaction.objectStore("Apps")从它那拿到在你创建事务前已经指定过的对象存储空间Apps,再调用add()方法增加你需要的数据和delete方法从数据库中删除数据。

本地化存储一直是本地客户端程序优于web应用程序的一个方面,HTML5本地存储使得web应用能够将网站数据持久地存储到本地终端并在需要的时候通过JS随时获取,而且比cookie的容量大得多,掌握好它无疑是我们必备的技能之一。以上只是对各种本地存储解决方案做了简单的介绍,还是需要大家在实践中做更详细的学习。



转载自: 腾讯CUBE

你可能感兴趣的:(html5)