分享

HTML5之Web存储技术

 杰出天下 2012-06-26

Web存储是一个规范,也HTML5规范集的一部分。目前,Web存储只是一个草案,但是几个主流的浏览器(甚至是IE8)都已经实现了Web存储,只是命名有些不一样,“本地存储”或“DOM存储”。下面,我将解释什么是Web存储,以及怎样在Web程序或Web站点中使用Web存储。

Web存储诞生之前

在Web应用程序或Web站点中,以无状态的方式工作必须要保存数据和状态。在早期,我们使用缓存Cookies来保存数据。但是,使用Cookies有很多限制和不利因素。比如,对于每一个HTTP请求,只能有4KB的存储量,不能超过这个限制。Web存储就是为了解决此限制而兴起的规范。

什么是Web Storage?

Web存储是一个键/值字典,在浏览器中保存和持久化数据,即使你关闭了浏览器(从这点上看,类似于Cookies机制)。与Cookies机制不同的是,Web存储的数据是供本地浏览器使用,而不是发送到服务器端。而且,在规范中对Web存储的本地磁盘空间没做限制。但是,实际上浏览器会限制Web存储的数量。

Web存储分为两种不同的存储对象:

1、会话存储Session Storage:在窗口中同一网站的任何页面都可以访问它存储的数据。

2、本地存储Local Storage:数据可跨越多个窗口,无视当前会话,被共同访问、使用。

每个Web应用程序/Web站点都有它自己专门的存储。

Web存储API

Web存储API包括下列方法:

length – 存储的键/值对的数量。

key(n) – 返回存储的第N个键。

getItem(key) – 返回键key对应的值。如果值不存在,则返回空null。注意,返回的值是一个字符串。如果你存储的值是整数或布尔型,你需要类型转换。

setItem(key, value) – 把值插入到key键中。

removeItem(key) – 移除某个键对应的值(包含键本身)。如果键不存在,此方法什么也不做。

clear – 清空存储的键/值数据。

下面是本地存储中get和set键值项的例子:

  1. localStorage.setItem("key", "value);
  2. var val = localStorage.getItem("key");         
复制代码


还可以不使用get和set方法来存取键值数据。你可以直接把存储看做是一个带属性的类,直接访问其属性。
  1. localStorage.key = "value";
  2. var val = localStorage.key;       
复制代码


一些浏览器把Web存储看做是一个字典(或JavaScript数组),通过数组的序号访问数据:
  1. localStorage["key"] = "value";
  2. var val = localStorage["key"];         
复制代码



但是,目前序号的访问方式在规范中还没有(浏览器厂家自行定义的,未来在规范中或许会加上)。

Web存储的另一个方面是存储事件。当在Web存储中出现改变时,存储事件被触发。存储事件关联到窗口对象,无论是setItem()方法、removeItem()方法,或者是clear()方法,都会触发该事件。开发者可以为事件写一个接收事件参数的函数:
  1. if (window.addEventListener) {
  2.     window.addEventListener("storage", handleStorageEvent, false);
  3. } else {
  4.     // IE9以下的版本
  5.     window.attachEvent("onstorage", handleStorageEvent);
  6. };
  7. function handleStorageEvent(eventData) {
  8.     // 做一些工作
  9. }         
复制代码



上面例子中的eventData参数,可以保存下列数据:

key – 要修改的键;

oldValue – 要修改的键对应的旧值;

newValue – 要修改的键对应的新值;

url – 要修改的键的文档的地址;

storageArea – 存储的区域。

实战例子

下面的例子,我将使用本地存储LocalStorage,目的是保存页载入事件,并把它展现给用户:
  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  2.   "http://www./TR/xhtml1/DTD/xhtml1-transitional.dtd">
  3. <html xmlns="http://www./1999/xhtml">
  4.   <head runat="server">
  5.     <title></title>
  6.     <script type="text/javascript">
  7.     function pageLoadCounter() {
  8.       if (!localStorage.getItem('loadCounter')) {
  9.         localStorage.setItem('loadCounter', 0);
  10.       }
  11.       localStorage.setItem('loadCounter', parseInt(localStorage.getItem('loadCounter')) + 1);
  12.       document.getElementById('counter').innerHTML = localStorage.getItem('loadCounter');
  13.     }
  14.     </script>
  15.   </head>
  16.   <body onload="pageLoadCounter()">
  17.     <form id="form1">
  18.       <p>You have viewed this page <span id="counter"></span> times.</p>
  19.       <p><input type="button" onclick="localStorage.clear();" value="Clear Storage" /></p>
  20.     </form>
  21.   </body>
  22. </html>         
复制代码


这个例子很简单,但它展示了一些API方法的用法,如getItem()、setItem()和clear()。你可以看到,存储的数据是字符串方式,必须进行类型转换后再使用。

在例子中,我把counter数据转换成整数,使用了parseInt()方法。你可以关闭浏览器或Tab页,然后打开它,查看最后持久化存储的counter数据。

总结

Web存储是一个新规范,它试图对客户端的数据进行保存。在客户端还有其他方法保存数据,比如Cookies机制或使用Google Gears框架内嵌的数据库(它基于SQLite)。 

HTML5 离线存储之Web SQL

本篇没有考虑异步,多线程及SQL注入

WebDatabase 规范中说这份规范不再维护了,原因是同质化(几乎实现者都选择了Sqlite), 
且不说这些,单看在HTML5中如何实现离线数据的CRUD,最基本的用法(入门级别) 
 
1,打开数据库 
2,创建表 
3,新增数据 
4,更新数据 
5,读取数据 
6,删除数据 

事实上,关键点在于如何拿到一个可执行SQL语句的上下文, 
像创建表,删除表,CRUD操作等仅区别于SQL语句的写法.OK,貌似"SqlHelper"啊,换个名字,dataBaseOperator就它了

executeReader,executeScalar两个方法与executeNonQuery严重同质,

下边的代码产生定义了我们的dataBaseOperator"类",第二行
3-5行则定义打开数据库连接方法,"类方法",效果类似C#中的静态方法,直接类名.方法调用
6-15行则定义executeNonQuery方法,意指查询数据库,与executeReader方法和executeScalar方法同质,均可返回记录集
整个 dataBaseOperator就完整了,很简单,唯一要指出的是,测试以下代码时请选择一个支持HTML5的浏览器!如Google Chrome

1 //TODO;SQL注入
2  function dataBaseOperator() {};
3 dataBaseOperator.openDatabase = function () {
4 return window.openDatabase("dataBaseUserStories", "1.0", "dataBase used for user stories", 2 * 1024 * 1024);
5 }
6 dataBaseOperator.executeNonQuery = function (sql, parameters, callback) {
7 var db = this.openDatabase();
8 db.transaction(function (trans) {
9 trans.executeSql(sql, parameters, function (trans, result) {
10 callback(result);
11 }, function (trans, error) {
12 throw error.message;
13 });
14 });
15 }
16 dataBaseOperator.executeReader = dataBaseOperator.executeNonQuery;
17 dataBaseOperator.executeScalar = dataBaseOperator.executeNonQuery;

有了"SqlHeper",再看业务处理层(Business Logic Layer)
业务处理类包括了创建表,删除表,新增记录,删除记录以及读取记录,这里没有写更新,实际上先删后增一样滴,即使要写也不复杂 

1 function userStoryProvider() {
2 this.createUserStoryTable = function () {
3 dataBaseOperator.executeNonQuery("CREATE TABLE tbUserStories(id integer primary key autoincrement,role,ability,benefit,name,importance,estimate,notes)");
4 };
5 this.dropUserStoryTable = function () {
6 dataBaseOperator.executeNonQuery("DROP TABLE tbUserStories");
7 };
8 this.addUserStory = function (role, ability, benefit, name, importance, estimate, notes) {
9 dataBaseOperator.executeNonQuery("INSERT INTO tbUserStories(role,ability,benefit,name,importance,estimate,notes) SELECT ?,?,?,?,?,?,?",
10 [role, ability, benefit, name, importance, estimate, notes], function (result) {
11 //alert("rowsAffected:" + result.rowsAffected);
12   });
13 };
14 this.removeUserStory = function (id) {
15 dataBaseOperator.executeNonQuery("DELETE FROM tbUserStories WHERE id = ?", [id], function (result) {
16 //alert("rowsAffected:" + result.rowsAffected);
17   });
18 };
19 this.loadUserStories = function (callback) {
20 dataBaseOperator.executeReader("SELECT * FROM tbUserStories", [], function (result) {
21 callback(result);
22 });
23 //result.insertId,result.rowsAffected,result.rows
24   };
25 }

createUserStoryTable,dropUserStoryTable,addUserStory,removeUserStory又是严重同质,不说了,仅SQL语句不同而已

但loadUserStories与上述四个方法均不同,是因为它把SQLResultSetRowList返回给了调用者,这里仍然是简单的"转发",
 页面在使用的时候需要首先创建provider实例(使用类似C#中的类实例上的方法调用)

1 var _userStoryProvider = new userStoryProvider();

之后就可以调用该实例的方法了,仅举个例子,具体代码省去

function loadUserStory() {
try {
_userStoryProvider.loadUserStories(function (result) {
var _userStories = new Array();
for (var i = 0; i < result.rows.length; i++) {
var o = result.rows.item(i);
var _userStory = new userStory(o.id, o.name, o.role, o.ability, o.benefit, o.importance, o.estimate, o.notes);
_userStories.push(_userStory);
}
//...
} catch (error) {
alert("_userStoryProvider.loadUserStories:" + error);
}
}

得到_userStories这个数组后,就没有下文了,是自动创建HTML还是绑定到EXT,发挥想象力吧...继续

userStory是一个自定义的"Model" "类"

1 function userStory(id, name, role, ability, benefit, importance, estimate, notes) {
2 this.id = id;
3 this.name = name;
4 this.role = role;
5 this.ability = ability;
6 this.benefit = benefit;
7 this.importance = importance;
8 this.estimate = estimate;
9 this.notes = notes;
10 };

最后贴出应用的代码,业务相关的代码,不看也罢,谁家与谁家的都不同

1 /*
2 http:///questions/2010892/storing-objects-in-html5-localstorage
3 http://www./TR/webdatabase/#sqlresultset
4 http:///introducing-web-sql-databases/
5 http:///questions/844885/sqlite-insert-into-with-unique-names-getting-id
6  */
7  var _userStoryProvider = new userStoryProvider();
8 $(document).ready(function () {
9 loadUserStory();
10
11 /* 添加用户故事 */
12 $("#btnAdd").click(function () {
13 var item = { role: $("#role").val(), ability: $("#ability").val(), benefit: $("#benefit").val(), name: $("#Name").val(), importance: $("#Importance").val(), estimate: $("#Estimate").val(), notes: $("#Notes").val() };
14 try {
15 _userStoryProvider.addUserStory(item.role, item.ability, item.benefit, item.name, item.importance, item.estimate, item.notes);
16 loadUserStory();
17 } catch (error) {
18 alert("_userStoryProvider.addUserStory:" + error);
19 }
20 });
21
22 /* 创建用户故事表 */
23 $("#btnCreateTable").click(function () {
24 try {
25 _userStoryProvider.createUserStoryTable();
26 } catch (error) {
27 alert("_userStoryProvider.createUserStoryTable:" + error);
28 }
29 });
30
31 /* 删除用户故事表 */
32 $("#btnDropTable").click(function () {
33 try {
34 _userStoryProvider.dropUserStoryTable();
35 } catch (error) {
36 alert("_userStoryProvider.dropUserStoryTable:" + error);
37 }
38 });
39 });
40
41  /* 加载用户故事 */
42 function loadUserStory() {
43 try {
44 _userStoryProvider.loadUserStories(function (result) {
45 var _userStories = new Array();
46 for (var i = 0; i < result.rows.length; i++) {
47 var o = result.rows.item(i);
48 var _userStory = new userStory(o.id, o.name, o.role, o.ability, o.benefit, o.importance, o.estimate, o.notes);
49 _userStories.push(_userStory);
50 }
51
52 if (!_userStories) return;
53 var table = document.getElementById("user_story_table");
54 if (!table) return;
55 var _trs = table.getElementsByTagName("tr");
56 var _len = _trs.length;
57 for (var i = 0; i < _len; i++) {
58 table.removeChild(_trs[i]);
59 }
60 {
61 var tr = document.createElement("tr");
62 tr.setAttribute("class", "product_backlog_row header");
63 {
64 tr.appendChild(CreateTd("id", "id"));
65 tr.appendChild(CreateTd("name", "name"));
66 tr.appendChild(CreateTd("importance", "importance"));
67 tr.appendChild(CreateTd("estimate", "estimate"));
68 tr.appendChild(CreateTd("description", "role"));
69 tr.appendChild(CreateTd("notes", "notes"));
70 tr.appendChild(CreateTd("delete", "delete"));
71 };
72 table.appendChild(tr);
73 }
74 for (var i = 0; i < _userStories.length; i++) {
75 CreateRow(table, _userStories[i]);
76 }
77 });
78 } catch (error) {
79 alert("_userStoryProvider.loadUserStories:" + error);
80 }
81 }
82 function CreateRow(table, userStory) {
83 if (!table) return;
84 if (!userStory) return;
85 {
86 var tr = document.createElement("tr");
87 tr.setAttribute("class", "product_backlog_row");
88 {
89 tr.appendChild(CreateTd("id", userStory.id));
90 tr.appendChild(CreateTd("name", userStory.name));
91 tr.appendChild(CreateTd("importance", userStory.importance));
92 tr.appendChild(CreateTd("estimate", userStory.estimate));
93 tr.appendChild(CreateTd("description", userStory.role));
94 tr.appendChild(CreateTd("notes", userStory.notes));
95 tr.appendChild(CreateDeleteButton("delete_button", userStory.id));
96 };
97 table.appendChild(tr);
98 }
99 }
100 function CreateTd(name, value) {
101 var td = document.createElement("td");
102 td.setAttribute("class", "user_story " + name);
103 td.innerText = value;
104 return td;
105 };
106 function CreateDeleteButton(name, id) {
107 var td = document.createElement("td");
108 td.setAttribute("class", "user_story " + name);
109 /* 删除用户故事 */
110 td.innerHTML = "<a href=\"###\" title=\"delete\" onclick=\"javascript:_userStoryProvider.removeUserStory(\'" + id + "');removeRow(this);\">>>delete</a>";
111 return td;
112 }
113 function removeRow(obj) {
114 document.getElementById("user_story_table").deleteRow(obj.parentNode.parentNode.rowIndex);
115 //obj.parentNode.parentNode.removeNode(true);
116 }

 

有一个小例子,点这里下载(占位,有点小毛病改好就放)

看完代码复习下基本功课

1,WindowDatabase接口,注意openDatabase方法

 

[Supplemental, NoInterfaceObject]
interface WindowDatabase {
Database openDatabase(in DOMString name, in DOMString version, in DOMString displayName, in unsigned long estimatedSize, in optional DatabaseCallback creationCallback);
};
Window implements WindowDatabase;

[Supplemental, NoInterfaceObject]
interface WorkerUtilsDatabase {
Database openDatabase(in DOMString name, in DOMString version, in DOMString displayName, in unsigned long estimatedSize, in optional DatabaseCallback creationCallback);
DatabaseSync openDatabaseSync(in DOMString name, in DOMString version, in DOMString displayName, in unsigned long estimatedSize, in optional DatabaseCallback creationCallback);
};
WorkerUtils implements WorkerUtilsDatabase;

[Callback=FunctionOnly, NoInterfaceObject]
interface DatabaseCallback {
void handleEvent(in Database database);
};

2,SQLTransaction接口,关注executeSql方法

typedef sequence<any> ObjectArray;

interface SQLTransaction {
void executeSql(in DOMString sqlStatement, in optional ObjectArray arguments, in optional SQLStatementCallback callback, in optional SQLStatementErrorCallback errorCallback);
};

[Callback=FunctionOnly, NoInterfaceObject]
interface SQLStatementCallback {
void handleEvent(in SQLTransaction transaction, in SQLResultSet resultSet);
};

[Callback=FunctionOnly, NoInterfaceObject]
interface SQLStatementErrorCallback {
boolean handleEvent(in SQLTransaction transaction, in SQLError error);
};

3,最后看下SQLResultSetRowList定义

interface SQLResultSetRowList {
readonly attribute unsigned long length;
getter any item(in unsigned long index);
};

和SQLResultSet定义

1 interface SQLResultSet {
2 readonly attribute long insertId;
3 readonly attribute long rowsAffected;
4 readonly attribute SQLResultSetRowList rows;
5 };

类图
webgl
由于window对象实现了WindowDatabase接口,从而可以直接在window对象上调用openDatabase等方法
同时实现WindowDatabse接口的还有WorkerUtils对象,点这里了解

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多