本地持久化存储一直是本地客户端程序优于 web 程序的一个方面。对于本地应用程序,操作系统会一共一个抽象层,用于存储和获取特定于应用程序的数据,例如用户设置或者运行时状态。这些值可以被存储于注册表、INI 文件,或者其他什么地方,这取决于操作系统的实现。如果你的本地应用程序需要不简单是键值对形式的本地存储,你也可以使用嵌入式数据库、发明你自己的文件格式,或者其他很多种解决方案。
然而,web 应用程序就没这些优点了。于是在 web 早期我们就发明了 cookie,目的是在本地持久存储少量数据。但是,cookie 有三个致命缺点:
- cookie 会包含进每一个 HTTP 请求,因此会减慢 web 应用程序,产生不必要的重复数据;
- cookie 会包含进每一个 HTTP 请求,因此网络上发送的数据就不能加密(除非你的整个应用都是用的 SSL)
- cookie 限制数据大小为 4 KB——这已经足以降低你的应用程序的速度,但是 4 KB 的大小有时候确实会捉襟见肘。
我们真正想要的是:
- 更大的存储空间
- 在客户端上的
- 不受页面刷新的影响
- 不需要提交到服务器
在 HTML5 之前,我们没有任何办法能够同时满足以上要求。
HTML5 之前的解决方案
一开始,我们的浏览器只有 Internet Explorer。或者说,那是 Microsoft 所期望的世界。到后来,在第一次浏览器大战期间,Microsoft 发明了一大堆东西,全部包含进了 Internet Explorer,而这个浏览器赢得了第一次浏览器大战。这其中之一就是 DHTML 行为(DHTML Behaviors),其中有一个行为叫做“用户数据 userData”。
userData 允许每一个域名的页面保存64KB数据,包括有层次结构的基于 XML 的结构。(受信域名,例如 intranet 站点,可以存储10倍于这个数字,也就是640KB。这几乎对任何人都已经足够了。)IE 不会有任何授权对话框,也不能增加额外的存储空间。
2002年,Adobe 在 Flash 6 引入了一项新的特性,却有一个明显会误导的名字:Flash cookie。在 Flash 环境下,这个特性被称为 Local Shared Object。简单来说,它允许每个域名的 Flash 对象存储100KB数据。Brad Neuberg 开发了一个 Flash-JavaScript 桥的早期原型,称为 AMASS(AJAX Massive Storage System),但它受限于 Flash 的设计怪癖。直到2006年,Flash 8 提供了 ExternalInterface,这么一来就可以由 JavaScript 直接访问 LSO,因此访问速度提升了一个数量级,变得更简单更快速。Brad 重写了 AMASS,并将其集成在 Dojo Toolkit 的 dojox.storage 中。Flash 给每个域名100KB的自由空间。如果超出,则允许按照数量级增加(1Mb、10Mb等等)。
2007年,Google 提供了 Gears,一个开源的浏览器插件,旨在为浏览器提供额外的功能。(我们曾经在为 IE 提供地理位置API的时候介绍过 Gears。)Gears 提供了一组基于 SQLite 的嵌入式数据库的 API。只要用户一次授权,Gears 能够按照域名在 SQL 数据库表中存储无限大小的数据。
与此同时,Brad Neuberg 和其他人继续 dojox.storage 的开发,以提供一种对这类插件和 API 的统一的接口。2009年,dojox.storage 能够自动检测(在其上层提供一种统一的接口)Adobe Flash、Gears、Adobe AIR 和 HTML5 存储的早期原型(仅有 Firefox 的一个较老版本实现)。
正如你所看到的这些解决方案,它们都有或多或少的问题:不是特定于某一浏览器,就是需要安装第三方插件。我们还需要对不同之处做一种“屏蔽”(正如 dojox.storage 所做的那样),它们有着不同的接口、不同的存储限制、不同的用户体验。这就是 HTML5 所要解决的问题:提供一种标准的 API,由多种浏览器提供原生支持,不需要安装第三方插件。
HTML5 存储简介
我所说的“HTML5 存储(HTML5 Storage)”,也就是标准上说的 Web Storage,这曾经是 HTML5 标准的一部分,后来由于某些不和谐的政治因素从 HTML5 分离,成为一个独立的标准。有些浏览器厂商也称为“本地存储 Local Storage” 或者 “DOM 存储 DOM Storage”。由于一些相关的问题、相似的名字、标准的合并问题等等,这个名字变得相当复杂,我们会再和后面的内容中仔细讲解。
那么,什么是 HTML5 Storage 呢?简单来说,就是一种让 web 页面能够以键值对的形式,在客户端web浏览器中将数据存储在本地的方法。就像 cookie 一样,这种数据在你离开 web 站点、关闭标签页、退出浏览器等等的时候依然保存。不同于 cookie 的地方是,这个数据不会被发送到远程 web 服务器(除非你自己手动发送)。另外,不同于我们前面所说的那些解决方案,这种机制是 web 浏览器原生提供的,所以不需要第三方插件的支持。
那么,什么浏览器才支持呢?目前,几乎所有主流浏览器的最新版本都支持 HTML5 Storage 了,甚至包括 Internet Explorer!
IE |
Firefox |
Safari |
Chrome |
Opera |
iPhone |
Android |
8.0+ |
3.5+ |
4.0+ |
4.0+ |
10.5+ |
2.0+ |
2.0+ |
我们可以使用 JavaScript 来访问 HTML5 Storage,通过全局的 window 对象的 localStorage 对象。在我们使用之前,我们需要首先检测是否可用:
function supports_html5_storage() {
try {
return 'localStorage' in window && window['localStorage'] !== null;
} catch (e) {
return false;
}
}
当然,我们也可以使用 Modernizr 检测:
if (Modernizr.localstorage) {
// window.localStorage is available!
} else {
// no native support for HTML5 storage :(
// maybe try dojox.storage or a third-party solution
}
使用 HTML5 Storage
HTML5 Storage 基于键值对存储。你要存储的数据需要有一个名字作为键,然后你就可以使用这个键读取这个数据。这个键是一个字符串;数据则可以是 JavaScript 支持的任何数据类型,包括字符串、布尔值、整数和浮点数。但是,我们通常将数据作为字符串进行存储。如果你存储读取非字符串数据,你就得使用类似 parseInt() 或者 parseFloat() 这样的函数,将读取的数字转换成所需要的 JavaScript 数据类型。
interface Storage {
getter any getItem(in DOMString key);
setter creator void setItem(in DOMString key, in any data);
};
调用 setItem() 时需要有一个键作为参数。如果这个键已经存在,则原有的值将被覆盖。getItem() 也要有一个键作为参数,如果该键不存在,则会直接返回 null,不会引发异常。
正如其他的 JavaScript 对象,你可以将 localStorage 对象作为一个关联数组。除了使用 getItem() 和 setItem() 函数,也可以直接使用方括号语法。例如:
var foo = localStorage.getItem("bar");
// ...
localStorage.setItem("bar", foo);
也可以写成:
var foo = localStorage["bar"];
// ...
localStorage["bar"] = foo;
当然也有函数,用于删除已有的值,清空整个存储区域(也就是一次性将所有键和值全部删除)等。
interface Storage {
deleter void removeItem(in DOMString key);
void clear();
};
调用 removeItem() 时传入一个不存在的键不会做任何动作。
最后,有一个属性可以获得存储区域中值的总数,借助另外一个函数则可以使用索引进行遍历(获取每个键的名字)。
interface Storage {
readonly attribute unsigned long length;
getter DOMString key(in unsigned long index);
};
调用 key() 的索引如果不在 0 – (length-1) 之间,函数将返回 null。
HTML5 Storage 变化追踪
如果你需要以编程方式跟踪存储区域的改变,你需要使用 storage 事件。storage 事件在 window 对象调用 setItem()、removeItem() 或者 clear(),并且的确有东西改变的时候会被派发。例如,如果你设置一个已存在的值,或者在一个空的存储区域调用 clear() 函数,因为没有任何动作,所以就不会触发 storage 事件。
只要支持 localStorage 对象的地方都会支持storage 事件,包括 IE8。IE 8 不支持 W3C 的标准的 addEventListener(事实上这一函数在 IE 9 才被加入)。因此,为了监听 storage 事件,你需要检测浏览器支持哪种事件机制(如果你了解这个内容,就可以跳过这部分了。处理 storage 事件同其他时间一样。如果你使用 jQuery 或者其他 JavaScript 库的事件处理函数,那么你也可以这么处理 storage 事件。)
if (window.addEventListener) {
window.addEventListener("storage", handle_storage, false);
} else {
window.attachEvent("onstorage", handle_storage);
};
handle_storage 回调函数接受一个 StorageEvent 参数。在 Internet Explorer 中,event 对象则存在 window.event 里面。
function handle_storage(e) {
if (!e) { e = window.event; }
}
此时,变量 e 就是一个 StorageEvent 对象。这个对象有很多有用的属性。
StorageEvent 对象 |
属性 |
类型 |
说明 |
key |
string |
增加、删除或者修改的那个键 |
oldValue |
any |
改写之前的旧值,如果是新增的元素,则是 null |
newValue |
any |
改写之后的新值,如果是删除的元素,则是 null |
url* |
string |
触发这个改变事件的页面 URL |
*注意:url 属性最早叫做 uri。有些浏览器在标准修改之前就已经发布了。为了最大兼容,你应该检测是否存在这个 url 属性,如果没有,则要检测 uri 属性。 |
storage 事件不能取消。在 handle_storage 回调函数中,没有方法能够终止事件。浏览器只会简单地告诉你,“就是发生了!你现在什么也干不了了!我只是告诉你,它就是发生了。”
via:
DevBean标签:HTML5浏览器相关本地存储