HTML5本地存储(Local Storage) 的前世此生

长久以来本地存储能力一直是桌面应用区别于Web应用的一个主要优点.对于桌面应用(或者原生应用),操做系统通常都提供了一个抽象层用来帮助应用程序保存其本地数据

例如(用户配置信息或者运行时状态等). 常见的存放这些数据的方式有许多: 注册表,INI文件,XML文件等等。 除了上面这些比较简单的用来存放 键值对的存储形式,如
果你须要使用更加复杂强大的存储,那么你还能够进一步在应用程序中嵌入小型数据库,或者开发出特定的数据文件格式。

javascript

遗憾的是,上面这些本地存储方法对Web应用来讲都是不适用的。在Web的发展史上,在很长时间里 Cookies是惟一可使用的在用户本地存储少许数据的方法。 但Cookies有一些很是明显的缺陷,限制了它的应用:html

1. cookie会被附加在每一个HTTP请求中,因此无形中增长了流量。html5

2. 因为在HTTP请求中的cookie是明文传递的,因此安全性成问题。 (除非你的整个应用都是使用SSL来构建的)
3. Cookie的大小限制在4 KB左右。 对于复杂的存储需求来讲是不够用的。


对于开发者来讲,他们真正须要的是

1. 不受限的存储空间

2. 数据保存在客户端

3. 数据的生命周期能够跨越页面的刷新(甚至浏览器关闭从新打开)

4. 本地数据没必要每次都被重复的传回服务器而致使流量增长

在HTML5以前,为了达成上述目标,人们开发出了许多方法,可是老是有一些不尽人意之处。 

java

在HTML5以前的本地存储的简史


在互联网发展早期,浏览器市场还很单一(处在领先地位的只有Netscape浏览器和IE)。在第一次浏览器大战中,微软的IE为了争取更大的份额,它本身发明了许多额外附加的功能。 这些功能就包括动态HTML (DHTML) , 而动态HTML中就包含了一种称为userData的技术。


userData 容许网页存储最大64KB的基于XML的结构化数据(每一个站点) 。获信的站点,例如内网站点,可以使用的存储量能够增大到10倍,也即640K。 在使用userData时,IE不会弹出任何形式的对话框来要求用户受权,也不容许程序增长本地存储的容量。


在2002年,Adobe(译者:那个时候其实应该是Macromedia) 在Flash6中引入了一个新的本地存储功能,并命名为“Flash Cookies"。 这个名称十分具备迷惑性,其实跟cookie没什么关系。 在Flash中,这个功能被称做 Local Shared Objects 。 简单来讲,这个技术容许Flash 对象存储100KB的数据(每一个站点 )。 基于此, Brad Neuberg 开发了一个称为 AMASS(AJAX Massive Storage System) Ajax大容量存储系统)的 Flash到JavaScript的桥接原型接口,容许开发者在JavaScript中调用LSO,可是因为Flash的种种技术局限,这个原型并不大好 用。 到2006年,随着Flash 8 引入了ExternalInterface技术,在JavaScript中访问LSO对象变得简化了许多。这时Brad重写了AMASS并把它整合到了流行的Dojo Toolkit 框架中, 并正式命名为dojox.storage。 Flash的这种技术容许每一个站点存储100KB的数据,超过100KB,则每增长超过一个数量级(如1MB, 10MB 等),它就会弹出对话框来要用户确认并受权。


在2007年,Google启动了著名的Gears项目, Gears是一个经过插件技术来加强浏览器功能的开源项目。 Gears提供了一套API来访问一个基于SQLite的嵌入式SQL数据库, 在得到用户的一次性受权后,应用程序能够经过Gears存储不限数量的本地数据。 

与 此同时, Brad Neuberg 和其余人继续开发dojox.storage , 但愿可以提供一套统一的JavaScript接口来封装上面各类插件和接口。 到2009年时, dojox.storage 已经能够作到自动的侦测用户浏览器所支持的本地存储技术,并提供统一的访问接口,包括Adobe Flash, Adobe AIR, Gears 以及早期 Firefox浏览器所提供的HTML5 存储功能。

从咱们前面的介绍能够看到这些五花八门的技术都有一个问题,他们要么是某 个浏览器所特有的技术,要么依赖于某个第三方插件(flash或Gears). 虽然Dojox.storage很是有远见的试图去封装这些区别,可是用户仍然会由于底层技术的限制而在用户体验,容许存储的数据量等方面没法统一。 这时,只有HTML5标准的出现才能完全解决这些问题:  提供一套标准化的API, 被绝大多数浏览器支持,不用依赖任何第三方插件。 



程序员

HTML5 本地存储简介


这里咱们称为HTML Storage的其实是一个称为 Web Storage  的标准, 它原来曾是HTML5标准的一部分,但因为某些政治因素,如今它被独立出来。  某些浏览器厂商也称它为 本地存储(local storage),或者DOM存储 (DOM Storage)

那 么究竟什么是HTML5本地存储 ? 简单来讲,它就是一种让网页能够把键值对存储在用户浏览器客户端的方法。像Cookie同样,这些数据不会由于你打开新网站,刷新页面,乃相当闭你的浏览 器而消失。 而与Cookie不一样的时,这些数据不会每次随着HTTP请求被发送到服务器端(固然若是你须要这么作,你能够本身编程实现 ). 由于这是HTML5规范的一部分,这一接口会被浏览器原生支持,不用依赖任何第三方插件。 

那么,如今有哪些浏览器支持这一接口呢? 在这篇文章写做时(译者:2011年2月) 差很少全部主流浏览器的最新版都支持了,连IE8都支持了。
web

HTML5  Storage support
IE Firefox Safari Chrome Opera iPhone Android
8.0+ 3.5+ 4.0+ 4.0+ 10.5+ 2.0+ 2.0+

 

在你的JS代码中,你能够经过winow.localStorage 对象来访问HTML5 本地存储功能. 固然,考虑到浏览器兼容性,你在使用前应该先侦测一下你的用户的浏览器是否支持.ajax

function supports_html5_storage() {
  try {
    return 'localStorage' in window && window['localStorage'] !== null;
  } catch (e) {
    return false;
  }
}





另外一种方式是使用Modernizr (译者:一个开源的用来侦测用户浏览器对HTML5支持度的工具) 来侦测sql

if (Modernizr.localstorage) {
  // window.localStorage is available!
} else {
  // 浏览器不支持HTML5 storage :(
  // 能够考虑使用dojox.storage 或其余方法
}






如何使用HTML5 存储


HTML5 存储是基于键值对的。数据存储在一个键里,访问数据时能够根据一样的键得到上次存储的数据. 键是一个字符串. 而数据则能够是任何类型的JavaScript基本数据类型,包括 字符串,Boolean,整数,和浮点数. 不过须要注意的是,这些数据在存储时其实是以字符串保存的。 所以在访问数据时你须要利用parseInt() 或 parseFloat()方法来作数据类型的转换。

数据库

interface Storage {
  getter any getItem(in DOMString key);
  setter creator void setItem(in DOMString key, in any data);
};



若是在调用setItem 时使用一个已经存在的键,将会直接覆盖掉该键上保存的值。而调用getItem时传入一个不存在的键,则会返回一个null ,不会抛出异常。


编程

就像其余JavaScript对象同样,你也能够将localStorage对象当成是关联数组使用(associative map)

除了使用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传入一个不存在的key则无操做,也不会有异常。 


最后,还提供了一个length属性来指示存储区域中所包含的全部键值对的数量 以及遍历全部键的key方法

interface Storage {
  readonly attribute unsigned long length;
  getter DOMString key(in unsigned long index);
};



若是你向key方法传入了一个越界的值(不在0到length-1 之间),则该方法返回null。

 

追踪HTML5 存储区域中的数据变化


除了经常使用的存取数据的方法,开发者还须要可以侦测数据变化的编程接口。这就是存储事件(storage event )
当 setItem(),removeItem()或者clear() 方法被调用,而且数据真的发生了改变时,storage事件就会被触发。注意这里的的条件是数据真的发生了变化。也就是说,若是当前的存储区域是空的,你 再去调用clear()是不会触发事件的。或者你经过setItem()来设置一个与现有值相同的值,事件也是不会触发的。 

全部支持localStorage对象的浏览器都支持存储事件,也包括IE8。 不过因为IE8不支持W3C标准的addEventListener (IE9 支持)。所以要在不一样浏览器中侦听存储事件,仍然须要一些代码来兼顾浏览器之间事件处理机制的不一样。
固然你也可使用jQuery,Dojo 或者其余Javacript类库来帮你注册事件处理函数,存储事件也是能够支持的。

if (window.addEventListener) {
  window.addEventListener("storage", handle_storage, false);
} else {
  window.attachEvent("onstorage", handle_storage);
};



上面代码中handle_storage 是在存储事件发生时被调用的回调函数,传入参数是StorageEvent。 在IE中,该event对象会被保存在window.event 中。 

function handle_storage(e) {
  if (!e) { e = window.event; }
}



StorageEvent对象会包含下列的属性。

StorageEvent 对象
属性 类型 描述
key string 被修改的键。
oldValue any 修改前的值(若是是增长新的键值,则该属性为null)
newValue any 修改后的值(若是是删除键值,则该属性为null)
url* string 触发当前存储事件的页面的url
* 注意: url 属性早期的规范中为uri属性。有些浏览器发布较早,没有包含这一变动。为兼容性考虑,使用url属性前,你应该先检查它是否存在,若是没有url属性,则应该使用uri属性


要注意一点,在存储事件的处理函数中是不能取消这个存储动做的。存储事件只是浏览器在数据变化发生以后给你的一个通知。

目前技术的局限


前面的章节中, 我提到了过去许多用来实现浏览器本地存储的技术和插件的缺点,例如存储容量的限制。其实HTML5本地存储标准也有它自身的局限。简单来讲就是这几个关键词,“5M容量"和 “QUOTA_EXCEEDED_ERR“ 。

“5M 容量",是每一个来源(origin)(http://www.whatwg.org/specs/web-apps/current-work /multipage/origin-0.html#origin-0)容许存储容量的默认限制。在HTML5 存储标准中,5M只不过是做为一个建议的数值出现的,可是这个建议被全部的浏览器所采用。挺奇怪的,不是吗?  须要注意的是,存储的数据都是以字符串形 式保存的。所以若是你存储了大量整型数或浮点数,这些数也会以字符串形式保存。浮点数的每一位都须要一个字符来表示。 这大大增长了所须要的存储空间。 

第二个关键词“QUOTA_EXCEEDED_ERR” 是一个异常,若是你使用的存储容量超过了5M,你就会碰到它。 

接 下来你天然会要问那若是我想使用超过5M的容量,是否是能够经过弹出对话框让用户受权的方式来增长容许容量么?很遗憾,答案是”不行!“ 在这篇文章写做时,(2月 2011), 没有浏览器支持容许程序向用户请求更大存储空间的机制。  有些浏览器(例如Opera)容许用户本身来控制每一个站点可以使用的存储容量,可是这必须由用户本身主动发起才行,做为开发者你没有办法来发起这样的请求。 


HTML5 本地存储 实战


这一节让咱们来实际操做一下HTML5 本地存储。 回忆一下咱们在canvas章节中开发的Halma 游戏。因为没有加入本地存储支持,每次关闭浏览器这个游戏的进度就会丢失。如今咱们能够用HTML5 本地存储技术在浏览器中保存玩家的游戏进度。这里是一个演示

试试看,在游戏中操做一些步骤,而后关闭该标签页而后在新标签页中再打开该游戏页面。若是你的浏览器支持HTML5本地存储,你会发现关闭前的游戏进度和状态被神奇的记录下来了。 


这是怎么实现的呢? 在游戏中,每次发生状态变化,咱们都会调用下面的方法:

 function saveGameState() {
    if (!supportsLocalStorage()) { return false; }
    localStorage["halma.game.in.progress"] = gGameInProgress;
    for (var i = 0; i < kNumPieces; i++) {
    localStorage["halma.piece." + i + ".row"] = gPieces[i].row;
    localStorage["halma.piece." + i + ".column"] = gPieces[i].column;
    }
    localStorage["halma.selectedpiece"] = gSelectedPieceIndex;
    localStorage["halma.selectedpiecehasmoved"] = gSelectedPieceHasMoved;
    localStorage["halma.movecount"] = gMoveCount;
    return true;
}



首先保存一个标志变量”gGameInProgress"用来指示当前是否有一个游戏正在进行。 而后是保存正在进行中的游戏的状态,包括哪些棋子被选中了,当前一共走的总步数等等 。 

在每次页面加载时,咱们会调用resumeGame()方法,这个方法会先检查本地存储中的halma.game.in.progress"标志,若是是true,则会恢复上次保存的游戏状态。若是false,则调用newGame开始新游戏。 

function resumeGame() {
    if (!supportsLocalStorage()) { return false; }
    gGameInProgress = (localStorage["halma.game.in.progress"] == "true");
    if (!gGameInProgress) { return false; }
    gPieces = new Array(kNumPieces);
    for (var i = 0; i < kNumPieces; i++) {
    var row = parseInt(localStorage["halma.piece." + i + ".row"]);
    var column = parseInt(localStorage["halma.piece." + i + ".column"]);
    gPieces[i] = new Cell(row, column);
    }
    gNumPieces = kNumPieces;
    gSelectedPieceIndex = parseInt(localStorage["halma.selectedpiece"]);
    gSelectedPieceHasMoved = localStorage["halma.selectedpiecehasmoved"] == "true";
    gMoveCount = parseInt(localStorage["halma.movecount"]);
    drawBoard();
    return true;
}




在上面resumeGame中最重要的一点就是“ 全部HTML5 本地存储中的数据都是以字符串形式保存的,若是你保存了其余数据类型,在访问这些数据时必定要本身对这些数据作强制类型转换"。 例如咱们保存的标志变量gGameInProgress是一个Boolean类型。 在saveGameState()中咱们不须要作任何特别操做

localStorage["halma.game.in.progress"] = gGameInProgress;


可是在resumeGame中,咱们要访问这个数据时,则必须作从string到Boolean的强制转换

gGameInProgress = (localStorage["halma.game.in.progress"] == "true");


相似的,处理数值类型,例如gMoveCount 变量,在saveGameStat中咱们用下面的语句保存一个整型变量

localStorage["halma.movecount"] = gMoveCount;


但在resumeGame方法中,则要使用JavaScript的parseInt函数来将这个值从字符串转换为整型

MoveCount = parseInt(localStorage["halma.movecount"]);

比键值对更强大的将来

相 比HTML5标准成型前的混乱状态,今天HTML5本地存储的情形史无前例的使人振奋。 新的API被标准化,而且被全部的主流浏览器平台和设备所支持。 做为看惯了各类不兼容的Web程序员,不多有机会看到如此美妙一致的现实。 不过5M的存储容量以及仅仅能存储键值对这种简单数据结构对于较复杂的应用来讲仍然是远远不够的。因此客户端持久化的将来仍然有许多发展的空间。而如今我 们已经能看到许多互相竞争的技术在浮现。 


其中一种技术的大名你也许早就听到过 "SQL". (译者: 这句笑话很冷啊) 2007年,Google发布Gears项目。Gears是一个跨浏览器的插件,其中就包含了一个基于SQLite的嵌入式数据库。 这一早期原型后来影响了Web SQL DataBase 规范的创建. 
WebSQLDataBase (以前也称做 WebDB) 在SQL DB之上提供了一层简单封装,从而容许你在JavaScript中使用下面的代码: 

(这可不是伪代码,下面的代码在4个浏览器里是真的能够工做的!)

 

openDatabase('documents', '1.0', 'Local document storage', 5*1024*1024, function (db) {
  db.changeVersion('', '1.0', function (t) {
    t.executeSql('CREATE TABLE docids (id, name)');
  }, error);
});



这段代码的核心就是传入 executeSql方法的SQL 语句。这里它可以支持全部的SQL语句,包括SELECTUPDATE, INSERT 和 DELETE。 一切都和后台的数据库编程没什么区别,惟一不一样的是你是在JavaScript里调用的! 太欢乐了!

Web SQL Database 标准已经在4个浏览器(以及对应的平台)中被实现了。

Web SQL Database supportIE    Firefox    Safari    Chrome    Opera    iPhone    Android
·    ·    4.0+    4.0+    10.5+    3.0+    2.0+

当 然,若是你对SQL略微有所了解的话,你就会明白其实“SQL” 这个词更多只是一个经常使用术语而不是一个真正统一的标准。( 这其实和HTML5 这个词是同样的 ) 固然,咱们读书时都学过一个SQL-92 标准,遗憾的是在现实世界里,没有一款产品能真正的严格符合这套标准。所以咱们知道有甲骨文的SQL,微软的SQL,MySQL的SQL, PostgreSQL的SQL, SQLite的SQL。 每一个厂商都在本身的数据库产品中加入了特有的SQL新特性。甚至在一个厂商的产品中,不一样的版本间SQL的用法也会产生变化。


这些SQL业界的纷争就致使了咱们看到在Web SQL Database 规范中出现了以下的免责声明:

本 规范如今碰到了一个难题: 全部的感兴趣的厂商目前都使用了一样的SQL后台(Sqllite), 可是咱们还须要有更多独立的实现来使本规范成为一个标准。目前本规范中所使用的SQL语法(SQL dialect)只是一个基于Sqlite所支持的SQL语法的参考,并不会做为未来标准的一部分。 

另外一个与此竞争的技术是 索引数据库(Indexed Database API ), 以前也称做WebSimpleDB, 不过如今更多人叫它IndexedDB


索 引数据库API主要的概念是对象存储容器(object store),对象存储容器很像SQL中的数据库概念。每一个数据库(object store)中都有不少记录(对象),每一个记录都有不少字段(fields)。每一个字段都有其预约义好的数据类型。 你能够选择全部记录的子集,使用游标来遍历全部记录, 也提供了事务的概念来保证数据完整性和一致性。 

全部这些概念都和传统SQL数据 库编程很类似。最主要的区别在于,Object Store之上不能使用SQL语句这种结构化的查询语言。 你用不着构建像SELECT * from USERS where ACTIVE = 'Y' 这样的查询。 你要作的是经过object store 提供的方法在名为USERS的数据库上打开一个游标指针(cursor)来遍历全部记录,而后过滤掉那些不知足条件的记录,最后在剩下的记录上使用数据访 问方法获取你所须要的数据。 这篇索引数据库入门是一篇很好的教程,里面 详细比较了索引数据库和Web SQL 数据库

在本文写做时,索引数据库只是在FF4 的beta版中被实现了。 Mozilla已经声明他们永远不会去实现Web SQL 数据库. Google 则说他们正考虑在Chromium和Google Chrome上支持索引数据库
连微软也说索引数据库是"很好互联网解决方案"

那么你究竟能用索引数据库干点什么呢? 在目前的情况下,除了一些演示以外几乎什么也作不了。 可是谁知道1年以后会不会就大不同了呢。  请参考 深刻阅读 一节的资料和教程。


深刻阅读


HTML5 存储规范 

DOM存储简介 , MSDN 

Web Storage: easier, more powerful client-side data storage, Opera Developer Community   

DOM 存储,  Mozilla developer Center(注意: 这篇文章大部分是介绍FF的globalStorage对象的实现。该对象是在标准的localStorage对象出现前的原型。在FF3.5之 后,Mozilla加入了对标准localStorage接口的支持)

Unlock local storage for mobile Web applications with HTML5, a tutorial on IBM DeveloperWorks


 Brad Neuberg 在HTML5 规范出现前的一些工做

Web SQL Database:

IndexedDB:

相关文章
相关标签/搜索