淺談HTML 5的DOM Storage機(jī)制
在開發(fā) Web 應(yīng)用時(shí),開發(fā)者有時(shí)需要在本地存儲(chǔ)數(shù)據(jù)。當(dāng)前瀏覽器支持 cookie 存儲(chǔ),但其大小有 4KB 的限制。這對(duì)于一些 Ajax 應(yīng)用來說是不夠的。更多的存儲(chǔ)空間需要瀏覽器本身或是插件的支持,如 Google Gears 和 Flash。不過開發(fā)人員需要通過檢測(cè)當(dāng)前瀏覽器所支持的插件類型來使用對(duì)應(yīng)的接口。 HTML5 中新引入了 DOM Storage 機(jī)制,通過使用鍵值對(duì)在客戶端保存數(shù)據(jù),并且提供了更大容量的存儲(chǔ)空間。本文將詳細(xì)論述 HTML5 對(duì)本地存儲(chǔ)的支持,并對(duì)存儲(chǔ)事件綁定和數(shù)據(jù)存儲(chǔ)與 JSON 的結(jié)合使用進(jìn)行討論。當(dāng)一些老版本的瀏覽器不支持 DOM Storage 時(shí),可以考慮用其他的技術(shù)如 Dojo 來實(shí)現(xiàn)相同的功能。本文也會(huì)對(duì)其進(jìn)行簡(jiǎn)單的介紹。
HTML5 是下一代 HTML 標(biāo)準(zhǔn),開始吸引越來越多人的目光。HTML5 的 DOM Storage 機(jī)制提供了一種方式讓程序員能夠把信息存儲(chǔ)到本地的計(jì)算機(jī)上,在需要時(shí)獲取。這點(diǎn)和 cookie 相似,區(qū)別是 DOM Storage 提供了更大容量的存儲(chǔ)空間。
目前,在客戶端保存數(shù)據(jù)使用最多的是 cookie,但 cookie 的大小上限為 4KB,并且每次請(qǐng)求一個(gè)新頁面時(shí) cookie 都會(huì)被發(fā)送過去。更多的存儲(chǔ)空間需要瀏覽器本身或是插件的支持,例如只在 Internet Explorer 上使用的 userData,需要額外安裝插件的 Google Gears 和 Flash。現(xiàn)在,HTML5 提供了一種標(biāo)準(zhǔn)的接口,使程序員可以簡(jiǎn)單地訪問存儲(chǔ)的數(shù)據(jù)。由于鍵值對(duì)存儲(chǔ)在本地計(jì)算機(jī)上,在頁面加載完畢后可以通過 JavaScript 來操作這些數(shù)據(jù)。
DOM Storage
示例應(yīng)用程序:用戶注冊(cè)
本文使用的示例應(yīng)用程序是一個(gè)簡(jiǎn)單的用戶注冊(cè)過程,表單包含三個(gè)字段:name、age 和 address,我們將其拆分為兩個(gè)表單,分兩個(gè)頁面顯示。借助簡(jiǎn)化了的數(shù)據(jù)模型,主要介紹如何利用 DOM Storage 功能處理表單跨頁問題。
DOM Storage 兩個(gè)分類
DOM Storage 分為 sessionStorage 和 localStorage。
localStorage 對(duì)象和 sessionStorage 對(duì)象使用方法基本相同,它們的區(qū)別在于作用的范圍不同。sessionStorage 用來存儲(chǔ)與頁面相關(guān)的數(shù)據(jù),它在頁面關(guān)閉后無法使用。而 localStorage 則持久存在,在頁面關(guān)閉后也可以使用。
DOM Storage 接口
下面是 DOM Storage 的接口定義:
- interface Storage {
- readonly attribute unsigned long length;
- getter DOMString key(in unsigned long index);
- getter any getItem(in DOMString key);
- setter creator void setItem(in DOMString key, in any data);
- deleter void removeItem(in DOMString key);
- void clear();
- };
length:返回當(dāng)前存儲(chǔ)在 Storage 對(duì)象中的鍵值對(duì)數(shù)量。
key(index):返回列表中第 n 個(gè)鍵的名字。Index 從 0 開始。
getItem(key):返回指定鍵對(duì)應(yīng)的值。
setItem(key, value):存入一個(gè)鍵值對(duì)。
removeItem(key) :刪除指定的鍵值對(duì)。
clear():刪除 Storage 對(duì)象中的所有鍵值對(duì)。
通常,使用最多的方法是 getItem 和 setItem。
以 sessionStorage 為例:
存儲(chǔ)鍵值對(duì):
window.sessionStorage.setItem(“key1”, value1);
通過鍵名來讀取值:
var value1 = window.sessionStorage.getItem(“key1”);
判斷瀏覽器是否支持 DOM Storage
要使用 DOM Storage,首先,需要查看當(dāng)前的瀏覽器是否支持。目前 Internet Explorer 8.0 以上,F(xiàn)irefox 3.5 以上,Chrome 4.0 以上都是支持 DOM Storage 的。
如果瀏覽器不支持 DOM Storage,可以用其他的方法作為備選,本文還使用 Dojo 提供的 dojox.storage 模塊來實(shí)現(xiàn)相同的功能。
清單 1. 查看瀏覽器是否支持 DOM Storage
- //sessionStorage
- if(window.sessionStorage){
- alert(“support sessionStorage”);
- }else{
- alert(“not support sessionStorage”);
- // 不支持 sessionStorage
- // 用 dojox.storage 來實(shí)現(xiàn)相同功能
- }
- //localStorage
- if(window.localStorage){
- alert(“support localStorage”);
- }else{
- alert(“not support localStorage”);
- // 不支持 localStorage
- // 用 dojox.storage 來實(shí)現(xiàn)相同功能
- }
下面是用戶注冊(cè)的兩個(gè)表單。清單 2 中的第一個(gè)表單有兩個(gè)字段 name 和 age 需要用戶填寫內(nèi)容。填寫完后點(diǎn)擊 Next 按鈕進(jìn)入下一個(gè)頁面,此時(shí)函數(shù) saveToStorage 會(huì)被調(diào)用,把在該頁面輸入的兩個(gè)字段的值保存到 sessionStorage 對(duì)象中。
當(dāng)從下一個(gè)頁面退回到本頁面時(shí),使用 windows.onload 在加載頁面的時(shí)候?qū)?shù)據(jù)從 sessionStorage 中取出,并顯示在輸入框中,方便用戶修改。
另外,給對(duì)象賦值除了用 setItem 方法外,也可以用 window.sessionStorage.key1 = “value1”。
清單 2. 第一個(gè)表單頁面
- <script type="text/javascript">
- // 當(dāng)退回到第一個(gè)頁面時(shí),從 sessionStorage 得到用戶之前輸入的值并顯示在頁面,方便修改
- window.onload = function(){
- if (window.sessionStorage) {
- var name = window.sessionStorage.getItem("name");
- var age = window.sessionStorage.getItem("age");
- if (name != "" || name != null){
- document.getElementById("name").value = name;
- }
- if (age != "" || age != null){
- document.getElementById("age").value = age;
- }
- }else
- {
- // 不支持 sessionStorage,用 Dojo 實(shí)現(xiàn)相同功能
- }
- };
- // 將數(shù)據(jù)保存到 sessionStorage 對(duì)象中
- function saveToStorage() {
- //sessionStorage
- if (window.sessionStorage) {
- var name = document.getElementById("name").value;
- var age = document.getElementById("age").value;
- window.sessionStorage.setItem("name", name);
- window.sessionStorage.setItem("age", age);
- window.location.href="form2.html";
- } else {
- // 不支持 sessionStorage,用 Dojo 實(shí)現(xiàn)相同功能
- }
- }
- </script>
- <form action="./form2.html">
- <input type="text" name="name" id="name">
- <input type="text" name="age" id="age">
- <input type="button" value="Next" onclick="saveToStorage()"></input>
- </form>
清單 3 的第二個(gè)頁面有一個(gè) address 字段。當(dāng)用戶填寫完畢后,點(diǎn)擊 Submit 按鈕提交頁面,此時(shí) addStorageValue 函數(shù)被調(diào)用,把保存在 sessionStorage 中的 name 和 age 值先賦給當(dāng)前表單的兩個(gè)隱藏字段,隨后一起提交給下一個(gè)處理表單的頁面。最后調(diào)用 removeItem 函數(shù)刪除 name 和 age 值。
如果用戶需要修改第一個(gè)頁面填寫的內(nèi)容,可以點(diǎn)擊 Back 按鈕回到前一個(gè)頁面,用戶在前一個(gè)頁面已經(jīng)填寫的內(nèi)容會(huì)出現(xiàn)在 text 框中。
清單 3. 第二個(gè)表單頁面
- <script type="text/javascript">
- // 將保持在 sessionStorage 中的數(shù)據(jù)賦給表單的隱藏屬性
- function addStorageValue() {
- //sessionStorage
- if (window.sessionStorage) {
- var name = window.sessionStorage.getItem("name");
- var age = window.sessionStorage.getItem("age");
- document.getElementById("name").value = name;
- document.getElementById("age").value = age;
- window.sessionStorage.removeItem("name");
- window.sessionStorage.removeItem("age");
- } else {
- // 不支持 sessionStorage,用 Dojo 實(shí)現(xiàn)相同功能
- }
- }
- function backToPreviousForm() {
- window.location.href="form1.html";
- }
- </script>
- <form action="./form3.php" method="post">
- <input type="hidden" name="name" id="name">
- <input type="hidden" name="age" id="age">
- <input type="text" name="address" id="address">
- <input type="button" value="Back" onclick="backToPreviousForm()">
- <input type="submit" value="Submit" onclick="addStorageValue()"></input>
- </form>
// 將保持在 sessionStorage 中的數(shù)據(jù)賦給表單的隱藏屬性
function addStorageValue() {
//sessionStorage
if (window.sessionStorage) {
var name = window.sessionStorage.getItem("name");
var age = window.sessionStorage.getItem("age");
document.getElementById("name").value = name;
document.getElementById("age").value = age;
window.sessionStorage.removeItem("name");
window.sessionStorage.removeItem("age");
} else {
// 不支持 sessionStorage,用 Dojo 實(shí)現(xiàn)相同功能
}
}
function backToPreviousForm() {
window.location.href="form1.html";
}
#p#
使用 DOM Storage 需要注意的幾點(diǎn)
保存在 Storage 對(duì)象的數(shù)據(jù)類型
當(dāng)使用 DOM Storage 進(jìn)行本地存儲(chǔ)時(shí),任何數(shù)據(jù)格式在 Storage 對(duì)象中都以字符串類型保存,所以如果保存的數(shù)據(jù)不是字符串,在讀取的時(shí)候需要自己進(jìn)行類型的轉(zhuǎn)換。這里我們使用 JSON 將對(duì)象序列化之后再存儲(chǔ)。
JSON (JavaScript Object Notation) 是一種輕量級(jí)的數(shù)據(jù)交換格式。易于人閱讀和編寫,同時(shí)也易于機(jī)器解析和生成。目前,JSON 已經(jīng)是 JavaScript 標(biāo)準(zhǔn)的一部分,主流的瀏覽器對(duì) JSON 支持都非常完善。
本文用到兩個(gè)相關(guān)的函數(shù)
JSON.parse() 函數(shù)會(huì)把 JSON 對(duì)象轉(zhuǎn)換為原來的數(shù)據(jù)類型。
JSON.stringify() 函數(shù)會(huì)把要保存的對(duì)象轉(zhuǎn)換成 JSON 對(duì)象保存。
在清單 4 中,先把一個(gè)布爾型的數(shù)據(jù)存到 Storage 對(duì)象中,然后再取出,可以看到布爾類型的數(shù)據(jù)在取出的時(shí)候變?yōu)樽址=酉聛頁Q一種方式保存數(shù)據(jù),先用 JSON.stringify 方法序列化數(shù)據(jù),然后保存到 Storage 對(duì)象中,在取出的時(shí)候用 JSON.parse 方法進(jìn)行反序列化,可以看到讀取出的數(shù)據(jù)還是布爾類型。
另外,使用 JSON 保存一個(gè)字符串,通過 Chrome 的 Storage 工具,可以看到存入的字符串兩邊有雙引號(hào),這個(gè)雙引號(hào)表示存入的是一個(gè)字符串。當(dāng)用 JSON 表示一個(gè)簡(jiǎn)單的字符串時(shí),會(huì)在字符串兩邊加上雙引號(hào)。最后,該頁面加載后的輸出如下:
string1 boolean2 string3
清單 4. 使用 JSON 對(duì) DOM Storage 的復(fù)雜數(shù)據(jù)進(jìn)行處理
- // 生成一個(gè) Boolean 類型的變量 data1
- var data1 = new Boolean(true);
- // 不用 JSON 處理數(shù)據(jù)
- sessionStorage["key1"] = data1;
- if(sessionStorage["key1"] == "true"){
- // 從 Storage 對(duì)象讀取出來的數(shù)據(jù) data1 變?yōu)?nbsp;String 類型
- document.write("string1 ");
- }
- // 使用 JSON 處理數(shù)據(jù) data1
- sessionStorage["key2"] = JSON.stringify(data1);
- if(JSON.parse(sessionStorage["key2"]) == true){
- // 從 Storage 對(duì)象讀取的數(shù)據(jù) data1,用 JSON 將變量轉(zhuǎn)換為原來的 Boolean 類型
- document.write("boolean2 ");
- }
- // 生成一個(gè) String 類型的變量
- var data2 = new String("true");
- // 使用 JSON 處理數(shù)據(jù),在 Storage 對(duì)象中保存的是 “string”
- sessionStorage["key3"] = JSON.stringify(data2);
- data2 = JSON.parse(sessionStorage["key3"]);
- if(data2 == "true"){
- // 變量轉(zhuǎn)換回來還是 String 類型
- document.write("string3");
- }
使用 Chrome 瀏覽器可以查看當(dāng)前的 sessionStorage 和 localStorage 的鍵值對(duì)。在工具欄選擇“工具”到“開發(fā)人員工具”到“Resources”到“Local Storage”或“Session Storage”, 可以查看 key 和 value。
圖 1. Chrome 瀏覽器的 Storage 工具欄

綜上所述,我們可以如清單 5 一樣,在加載頁面的時(shí)候用 JSON 轉(zhuǎn)換數(shù)據(jù)類型,在離開頁面的時(shí)候?qū)?shù)據(jù)保存為 JSON 對(duì)象。這樣,保存在 Storage 中任何類型的數(shù)據(jù)在讀取的時(shí)候都可以轉(zhuǎn)換為原來的類型。
清單 5. 使用 JSON 對(duì) DOM Storage 的復(fù)雜數(shù)據(jù)進(jìn)行處理
- <script type="text/javascript">
- var value;
- function loadValue() {
- value1 = JSON.parse(window.sessionStorage.getItem(“key1”));
- }
- function saveValue() {
- window.sessionStorage.setItem(“key1”) = JSON.stringify(value1);
- }
- window.addEventListener(“load”, loadValue. true);
- window.add
var value;
function loadValue() {
value1 = JSON.parse(window.sessionStorage.getItem(“key1”));
}
function saveValue() {
window.sessionStorage.setItem(“key1”) = JSON.stringify(value1);
}
window.addEventListener(“l(fā)oad”, loadValue. true);
window.addEventListener(“unload”, saveValue. true);
空間大小
HTML5 的建議是每個(gè)網(wǎng)站提供給 Storage 的空間是 5MB,一般來說足夠存字符串。如果存入的數(shù)據(jù)太大,有些瀏覽器如 Chrome 會(huì)拋出 QUOTA_EXCEEDED_ERR 異常。所以雖然 DOM Storage 提供的空間比 cookie 要大很多,但在使用需要注意限制。
圖 2. Chrome 瀏覽器拋出異常

安全性
一般不要在客戶端存儲(chǔ)敏感的信息,使用 localStorage、globalStorage 等在客戶端存儲(chǔ)的信息都非常容易暴露。應(yīng)該在完成數(shù)據(jù)存儲(chǔ)后使用 clear 或者 removeItem 方法清除保存在 Storage 對(duì)象中的數(shù)據(jù)。
存儲(chǔ)事件驅(qū)動(dòng)
如果想在存儲(chǔ)成功或修改存儲(chǔ)的值時(shí)執(zhí)行一些操作,可以用 DOM Storage 接口提供的事件。可以使用如下方法注冊(cè)事件:
window.addEventListener(“storage”, handleStorageEvent, false);
存儲(chǔ)事件接口定義
- interface StorageEvent : Event {
- readonly attribute DOMString key;
- readonly attribute any oldValue;
- readonly attribute any newValue;
- readonly attribute DOMString url;
- readonly attribute Storage storageArea;
- void initStorageEvent(in DOMString typeArg, in boolean canBubbleArg,
- in boolean cancelableArg, in DOMString keyArg, in any oldValueArg,
- in any newValueArg, in DOMString urlArg, in Storage storageAreaArg);
- };
key:發(fā)生改變的鍵。
oldValue:鍵改變之前的值。
newValue:鍵改變之后的值。
url:觸發(fā)存儲(chǔ)事件的頁面 url。
在清單 6 中注冊(cè)完存儲(chǔ)事件后,當(dāng) sessionStorage 或者 localStorage 對(duì)象的值發(fā)生改變時(shí),會(huì)觸發(fā) handleStorageEvent 函數(shù),在頁面顯示發(fā)生改變的鍵和改變之前與之后的值。
清單 6. 添加存儲(chǔ)事件
- // 顯示存儲(chǔ)事件的相關(guān)內(nèi)容
- function handleStorageEvent(e) {
- document.write(“key” + e.key + “oldValue” + e.oldValue + “newValue” + e.newValue);
- }
- // 添加存儲(chǔ)事件監(jiān)聽
- window.addEventListener(“storage”, handleStorageEvent, false);
使用 Dojo 實(shí)現(xiàn)之前用戶注冊(cè)的功能
Dojo 是一個(gè) JavaScript 實(shí)現(xiàn)的開源工具包,很大程度上屏蔽了瀏覽器之間的差異性。Dojo 擴(kuò)展庫 (dojox) 是 Dojo 在其基本庫、核心庫和 Dijit 庫的基礎(chǔ)上提供的一個(gè)非常豐富的組件倉庫。本文用到的 dojox.storage 模塊能夠?qū)?shù)據(jù)保存在本地存儲(chǔ)中,實(shí)現(xiàn)和之前 DOM Storage 一樣的功能。
由于一些老版本瀏覽器不支持 HTML5,我們還可以用 Dojo 來實(shí)現(xiàn)之前用戶注冊(cè)的功能。相對(duì)于 HTML5 的 DOM Storage 接口,Dojo 的 dojox.storage.Provider 接口提供的方法更多。這里我們列出幾個(gè)常用的方法。
get(key, namespace):返回指定鍵對(duì)應(yīng)的值。
put(key, value, resultsHandler, namespace):存入一個(gè)鍵值對(duì)。
remove(key, namespace):刪除指定的鍵值對(duì)。
clear(namespace):刪除對(duì)象中的所有鍵值對(duì)。
現(xiàn)在對(duì)第一個(gè)表單的 JavaScript 代碼做部分修改,并在頁面中引入 dojox.storage 模塊。這樣,程序在不支持 HTML5 的瀏覽器中能夠通過調(diào)用 Dojo 提供的方法正常運(yùn)行。dojo.require("dojox.storage") 表示引入 dojox.storage 功能模塊。然后通過 dojox.storage.manager.isInitialized() 查看 dojox.storage.manager 是否已經(jīng)初始化,如果沒有的話,則需要等待其初始化完成之后,再進(jìn)行存儲(chǔ)操作。
清單 7. 經(jīng)過修改后的第一個(gè)表單頁面的部分代碼
- <script type="text/javascript">
- dojo.require("dojox.storage");
- // 當(dāng)退回到第一個(gè)頁面時(shí),從 Storage 中得到用戶之前輸入的值并顯示在頁面,方便修改
- // 這里先進(jìn)行 dojox.storage.manager 的初始化
- if(!dojox.storage.manager.isInitialized()){
- dojo.connect(dojox.storage.manager, "loaded", saveAndLoad);
- } else{
- dojo.connect(dojo, "loaded", saveAndLoad);
- }
- function saveAndLoad(){
- var name;
- var age;
- //sessionStorage
- if (window.sessionStorage) {
- name = window.sessionStorage.getItem("name");
- age = window.sessionStorage.getItem("age");
- if (name != "" || name != null){
- document.getElementById("name").value = name;
- }
- if (age != "" || age != null){
- document.getElementById("age").value = age;
- }
- }//dojox.storage
- else
- {
- name = dojox.storage.get("name");
- age = dojox.storage.get("age");
- if (typeof name != "undefined" ){
- document.getElementById("name").value = name;
- }
- if (typeof age != "undefined" ){
- document.getElementById("age").value = age;
- }
- }
- }
- // 保存數(shù)據(jù)
- function saveToStorage() {
- var name = document.getElementById("name").value;
- var age = document.getElementById("age").value;
- //sessionStorage
- if (window.sessionStorage) {
- window.sessionStorage.setItem("name", name);
- window.sessionStorage.setItem("age", age);
- }//dojox.storage
- else {
- dojox.storage.put("name", name);
- dojox.storage.put("age", age);
- }
- window.location.href="form2.html";
- }
- </script>
dojo.require("dojox.storage");
// 當(dāng)退回到第一個(gè)頁面時(shí),從 Storage 中得到用戶之前輸入的值并顯示在頁面,方便修改
// 這里先進(jìn)行 dojox.storage.manager 的初始化
if(!dojox.storage.manager.isInitialized()){
dojo.connect(dojox.storage.manager, "loaded", saveAndLoad);
} else{
dojo.connect(dojo, "loaded", saveAndLoad);
}
function saveAndLoad(){
var name;
var age;
//sessionStorage
if (window.sessionStorage) {
name = window.sessionStorage.getItem("name");
age = window.sessionStorage.getItem("age");
if (name != "" || name != null){
document.getElementById("name").value = name;
}
if (age != "" || age != null){
document.getElementById("age").value = age;
}
}//dojox.storage
else
{
name = dojox.storage.get("name");
age = dojox.storage.get("age");
if (typeof name != "undefined" ){
document.getElementById("name").value = name;
}
if (typeof age != "undefined" ){
document.getElementById("age").value = age;
}
}
}
// 保存數(shù)據(jù)
function saveToStorage() {
var name = document.getElementById("name").value;
var age = document.getElementById("age").value;
//sessionStorage
if (window.sessionStorage) {
window.sessionStorage.setItem("name", name);
window.sessionStorage.setItem("age", age);
}//dojox.storage
else {
dojox.storage.put("name", name);
dojox.storage.put("age", age);
}
window.location.href="form2.html";
}
清單 8. 經(jīng)過修改后的第二個(gè)表單頁面的部分代碼
- <script type="text/javascript">
- dojo.require("dojox.storage");
- // 將保存在 sessionStorage 中的數(shù)據(jù)賦給表單的隱藏屬性
- function addStorageValue() {
- var name;
- var age;
- //sessionStorage
- if (window.sessionStorage) {
- name = window.sessionStorage.getItem("name");
- age = window.sessionStorage.getItem("age");
- document.getElementById("name").value = name;
- document.getElementById("age").value = age;
- window.sessionStorage.removeItem("name");
- window.sessionStorage.removeItem("age");
- }//dojox.storage
- else {
- name = dojox.storage.get("name");
- age = dojox.storage.get("age");
- document.getElementById("name").value = name;
- document.getElementById("age").value = age;
- dojox.storage.remove("name");
- dojox.storage.remove("age");
- }
- }
- function backToPreviousForm() {
- window.location.href = "form1.html";
- }
- </script>
dojo.require("dojox.storage");
// 將保存在 sessionStorage 中的數(shù)據(jù)賦給表單的隱藏屬性
function addStorageValue() {
var name;
var age;
//sessionStorage
if (window.sessionStorage) {
name = window.sessionStorage.getItem("name");
age = window.sessionStorage.getItem("age");
document.getElementById("name").value = name;
document.getElementById("age").value = age;
window.sessionStorage.removeItem("name");
window.sessionStorage.removeItem("age");
}//dojox.storage
else {
name = dojox.storage.get("name");
age = dojox.storage.get("age");
document.getElementById("name").value = name;
document.getElementById("age").value = age;
dojox.storage.remove("name");
dojox.storage.remove("age");
}
}
function backToPreviousForm() {
window.location.href = "form1.html";
}
結(jié)束語
HTML5 中引入了 DOM Storage 機(jī)制用于存儲(chǔ)鍵值對(duì),它的設(shè)計(jì)目的是提供大規(guī)模、易用的存儲(chǔ)功能,并且程序員可以通過調(diào)用標(biāo)準(zhǔn)的接口,簡(jiǎn)單地訪問存儲(chǔ)的數(shù)據(jù)。目前,許多新版本的瀏覽器都支持 DOM Storage 功能。當(dāng)老版本的瀏覽器不支持 HTML5 提供的 DOM Storage 機(jī)制時(shí),可以考慮用 Dojo 來實(shí)現(xiàn)相同的功能。
原文:http://www.ibm.com/developerworks/cn/web/1107_gaoly_html5storage/index.html
【編輯推薦】