无论是为了离线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的属性和方法如下:
length :获取存储的键值对的数量
remainingSpace:获取存储空间剩余空间的大小(非标准,仅IE8.0+支持)
setItem(key, value) :将value值存储到本地的key字段
removeItem(key):删除指定key本地存储的值
getItem(key):获取指定key本地存储的值
clear():删除localStorage中存储的所有数据
key(index):根据索引获取一个指定位置的键名
key-value对的形式不仅结构简单,而且各个平台都支持得非常好。下面三种方式都可以将字符串"hello"存储到本地的data字段。
localStorage.setItem("data", "hello");
localStorage.data = "hello";
localStorage["data"] = "hello";
同理,获取存储在本地的值也有上面三种方法。当然,还需要强调的是WebStorage保存的是字符串,如果数据是对象,得调用JSON对象的stringify()方法将它转换为字符串再保存。你可以像下面这样做一个简单的封装:
var localData = {};
localData.setValue = function(key, value){
if(value != undefined){
localStorage.setItem(key, JSON.stringify(value));
}else{
if(localStorage.getItem(key)){
return JSON.parse(localStorage.getItem(key));
}else{
return null;
}
}
}
如果你的程序需要在不同页面访问存储在localStorage中的同一个键值,你可能需要判断这个值是否已经被其他页面改变了,这可以通过向浏览器注册storage事件来实现,它包含许多有用的属性:
storageAera: 表示存储类型(session或者local)
key: 发生改变项的key
oldValue: key原来的值
newValue: key改变后的值
url:导致key发生改变的url
看下面简单的示例:
window.addEventListener("storage", function(e){
console.log(e.key + "'s oldValue is" + e.oldValue);
console.log(e.key + "'s newValue is" + e.newValue);
}, false);
localStorage["name"] = "Mike"; //A页面
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对象中找到)
我们一起来看一个简单的打开或创建一个数据库的例子:
window.indexedDB=window.indexedDB||window.webkitIndexedDB||window.msIndexedDB||window.mozIndexedDB; //浏览器兼容
var request = indexedDB.open("MyTestDB");
request.onsuccess = function(event){
var db = request.result;
}
request.onerror = function(event){
console.log("open error and errorCode is"+ request.errorCode );
}
上面代码首先调用IndexedDB对象唯一的方法open()方法打开名为”MyTestDB”的数据库,如果不存在,则会被创建。open()方法不会立刻打开数据库或者开始一个事务,它会返回一个我们可以作为事件来处理的包含result或者错误值的IDBOpenDBRequest对象。该对象有success和error事件属性。如果成功打开,会触发success事件,我们可以在事件处理程序里保存request.result这个IDBDatabase实例以供后面使用,如果发生错误,比如用户不允许你的web应用访问以创建一个数据库,则error事件会在request上触发。
然后,我们可以创建一个对象存储空间:
var appData = [{appId:"9100", appName:"生活", appVersion:1},
{appId:"8920", appName:"娱乐", appVersion:2}];
request.onupgraddeneeded = function(event){
var db = event.target.result;
var objectStore = db.createObjectStore("Apps", {keyPath: "appId"});
objectSotre.createIndex("appName","appName", {unique:false});
for(var i in appData){
objectStore.add(appData[i]);
}
}
上述代码调用creatObjectStore()方法创建了一个名为Apps的对象存储空间,并且定义了一个使得存储空间中每个对象都是唯一的属性作为key path,这个属性就是appId。存储空间中的所有对象都必须拥有appId属性。此外,createIndex()方法创建了一个索引来通过appName查找app,可能会有重名的,所以不能使用unique索引。最后,调用存储空间对象的add()方法往里面添加数据。
接下来就是向我们之前创建的数据库中增加数据了。但是,在可以操作数据库之前,我们需要开始一个事务。事务来自于你创建的数据库对象,而且必须给它指定它需要跨越的对象存储空间。
var transaction = db.transaction(["Apps"], "readwrite");
transaction.oncomplete = function(event){
//当数据库添加数据完成时执行的操作
};
transaction.onerror = function(event){
//添加数据发生错误时的错误处理
};
var objectStore = transaction.objectStore("Apps");
for(var i in appData){
var request = objectStore.add(appData[i]);
}
var request2 = objectStore.delete("9100");
request2.onsuccess = function(event){
//删除数据成功后执行的操作
};
上面代码中的transaction()方法接收三个参数并返回一个事务对象。第一个参数是事务希望跨越的对象存储空间的列表,包含所有需要跨越的对象存储空间名,这里就是我们之前创建的对象存储空间Apps。后面两个参数可选,如果第二个参数未指定,默认表示得到只读事务。如果想进行写操作,用上面的"readwrite"指定。拥有了事务对象后,你就可以通过transaction.objectStore("Apps")从它那拿到在你创建事务前已经指定过的对象存储空间Apps,再调用add()方法增加你需要的数据和delete方法从数据库中删除数据。
本地化存储一直是本地客户端程序优于web应用程序的一个方面,HTML5本地存储使得web应用能够将网站数据持久地存储到本地终端并在需要的时候通过JS随时获取,而且比cookie的容量大得多,掌握好它无疑是我们必备的技能之一。以上只是对各种本地存储解决方案做了简单的介绍,还是需要大家在实践中做更详细的学习。
转自腾讯HTML5