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:

相關文章
相關標籤/搜索