浅谈 HTML5 的 DOM Storage 机制 (转)

在开发 Web 应用时,开发者有时需要在本地存储数据。当前浏览器支持 cookie 存储,但其大小有 4KB 的限制。这对于一些 Ajax
应用来说是不够的。更多的存储空间需要浏览器本身或是插件的支持,如 Google Gears 和
Flash。不过开发人员需要通过检测当前浏览器所支持的插件类型来使用对应的接口。 HTML5 中新引入了 DOM Storage
机制,通过使用键值对在客户端保存数据,并且提供了更大容量的存储空间。本文将详细论述 HTML5 对本地存储的支持,并对存储事件绑定和数据存储与 JSON
的结合使用进行讨论。当一些老版本的浏览器不支持 DOM Storage 时,可以考虑用其他的技术如 Dojo 来实现相同的功能。本文也会对其进行简单的介绍。

HTML5 是下一代 HTML 标准,开始吸引越来越多人的目光。HTML5 的 DOM Storage 机制提供了一种方式让程序员能够把信息存储到本地的计算机上,在需要时获取。这点和 cookie 相似,区别是 DOM Storage 提供了更大容量的存储空间。

目前,在客户端保存数据使用最多的是 cookie,但 cookie 的大小上限为 4KB,并且每次请求一个新页面时 cookie 都会被发送过去。更多的存储空间需要浏览器本身或是插件的支持,例如只在 Internet Explorer 上使用的 userData,需要额外安装插件的 Google Gears 和 Flash。现在,HTML5 提供了一种标准的接口,使程序员可以简单地访问存储的数据。由于键值对存储在本地计算机上,在页面加载完毕后可以通过 JavaScript 来操作这些数据。

DOM Storage

示例应用程序:用户注册

本文使用的示例应用程序是一个简单的用户注册过程,表单包含三个字段:name、age 和 address,我们将其拆分为两个表单,分两个页面显示。借助简化了的数据模型,主要介绍如何利用 DOM Storage 功能处理表单跨页问题。

DOM Storage 两个分类

DOM Storage 分为 sessionStorage 和 localStorage。

localStorage 对象和 sessionStorage 对象使用方法基本相同,它们的区别在于作用的范围不同。sessionStorage 用来存储与页面相关的数据,它在页面关闭后无法使用。而 localStorage 则持久存在,在页面关闭后也可以使用。

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:返回当前存储在 Storage 对象中的键值对数量。

key(index):返回列表中第 n 个键的名字。Index 从 0 开始。

getItem(key):返回指定键对应的值。

setItem(key, value):存入一个键值对。

removeItem(key) :删除指定的键值对。

clear():删除 Storage 对象中的所有键值对。

通常,使用最多的方法是 getItem 和 setItem。

以 sessionStorage 为例:

存储键值对:

window.sessionStorage.setItem(“key1”, value1);

通过键名来读取值:

var value1 = window.sessionStorage.getItem(“key1”);

判断浏览器是否支持 DOM Storage

要使用 DOM Storage,首先,需要查看当前的浏览器是否支持。目前 Internet Explorer 8.0 以上,Firefox 3.5 以上,Chrome 4.0 以上都是支持 DOM Storage 的。

如果浏览器不支持 DOM Storage,可以用其他的方法作为备选,本文还使用 Dojo 提供的 dojox.storage 模块来实现相同的功能。

清单 1. 查看浏览器是否支持 DOM Storage
 //sessionStorage
 if(window.sessionStorage){
    alert(“support sessionStorage”);
 }else{
    alert(“not support sessionStorage”);
    // 不支持 sessionStorage
    // 用 dojox.storage 来实现相同功能
 } 

 //localStorage
 if(window.localStorage){
    alert(“support localStorage”);
 }else{
    alert(“not support localStorage”);
    // 不支持 localStorage
    // 用 dojox.storage 来实现相同功能
 }

下面是用户注册的两个表单。清单 2 中的第一个表单有两个字段 name 和 age 需要用户填写内容。填写完后点击 Next 按钮进入下一个页面,此时函数 saveToStorage 会被调用,把在该页面输入的两个字段的值保存到 sessionStorage 对象中。

当从下一个页面退回到本页面时,使用 windows.onload 在加载页面的时候将数据从 sessionStorage 中取出,并显示在输入框中,方便用户修改。

另外,给对象赋值除了用 setItem 方法外,也可以用 window.sessionStorage.key1 = “value1”。

清单 2. 第一个表单页面
 <script type="text/javascript">
 // 当退回到第一个页面时,从 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 实现相同功能
    }
 }; 

 // 将数据保存到 sessionStorage 对象中
 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 实现相同功能
    }
 }
 </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 的第二个页面有一个 address 字段。当用户填写完毕后,点击 Submit 按钮提交页面,此时 addStorageValue 函数被调用,把保存在 sessionStorage 中的 name 和 age 值先赋给当前表单的两个隐藏字段,随后一起提交给下一个处理表单的页面。最后调用 removeItem 函数删除 name 和 age 值。

如果用户需要修改第一个页面填写的内容,可以点击 Back 按钮回到前一个页面,用户在前一个页面已经填写的内容会出现在 text 框中。

清单 3. 第二个表单页面
 <script type="text/javascript">
 // 将保持在 sessionStorage 中的数据赋给表单的隐藏属性
 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 实现相同功能
    }
 } 

 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>

使用 DOM Storage 需要注意的几点

保存在 Storage 对象的数据类型

当使用 DOM Storage 进行本地存储时,任何数据格式在 Storage 对象中都以字符串类型保存,所以如果保存的数据不是字符串,在读取的时候需要自己进行类型的转换。这里我们使用 JSON 将对象序列化之后再存储。

JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式。易于人阅读和编写,同时也易于机器解析和生成。目前,JSON 已经是 JavaScript 标准的一部分,主流的浏览器对 JSON 支持都非常完善。

本文用到两个相关的函数

JSON.parse() 函数会把 JSON 对象转换为原来的数据类型。

JSON.stringify() 函数会把要保存的对象转换成 JSON 对象保存。

在清单 4 中,先把一个布尔型的数据存到 Storage 对象中,然后再取出,可以看到布尔类型的数据在取出的时候变为字符串。接下来换一种方式保存数据,先用 JSON.stringify 方法序列化数据,然后保存到 Storage 对象中,在取出的时候用 JSON.parse 方法进行反序列化,可以看到读取出的数据还是布尔类型。

另外,使用 JSON 保存一个字符串,通过 Chrome 的 Storage 工具,可以看到存入的字符串两边有双引号,这个双引号表示存入的是一个字符串。当用 JSON 表示一个简单的字符串时,会在字符串两边加上双引号。最后,该页面加载后的输出如下:

string1 boolean2 string3

清单 4. 使用 JSON 对 DOM Storage 的复杂数据进行处理
 // 生成一个 Boolean 类型的变量 data1
 var data1 = new Boolean(true); 

 // 不用 JSON 处理数据
 sessionStorage["key1"] = data1;
 if(sessionStorage["key1"] == "true"){
    // 从 Storage 对象读取出来的数据 data1 变为 String 类型
    document.write("string1 ");
 } 

 // 使用 JSON 处理数据 data1
 sessionStorage["key2"] = JSON.stringify(data1);
 if(JSON.parse(sessionStorage["key2"]) == true){
    // 从 Storage 对象读取的数据 data1,用 JSON 将变量转换为原来的 Boolean 类型
    document.write("boolean2 ");
 } 

 // 生成一个 String 类型的变量
 var data2 = new String("true");
 // 使用 JSON 处理数据,在 Storage 对象中保存的是 “string”
 sessionStorage["key3"] = JSON.stringify(data2);
 data2 = JSON.parse(sessionStorage["key3"]);
 if(data2 == "true"){
    // 变量转换回来还是 String 类型
    document.write("string3");
 }

使用 Chrome 浏览器可以查看当前的 sessionStorage 和 localStorage 的键值对。在工具栏选择“工具”到“开发人员工具”到“Resources”到“Local Storage”或“Session Storage”, 可以查看 key 和 value。

图 1. Chrome 浏览器的 Storage 工具栏

综上所述,我们可以如清单 5 一样,在加载页面的时候用 JSON 转换数据类型,在离开页面的时候将数据保存为 JSON 对象。这样,保存在 Storage 中任何类型的数据在读取的时候都可以转换为原来的类型。

清单 5. 使用 JSON 对 DOM Storage 的复杂数据进行处理
 <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.addEventListener(“unload”, saveValue. true);
 </script>

空间大小

HTML5 的建议是每个网站提供给 Storage 的空间是 5MB,一般来说足够存字符串。如果存入的数据太大,有些浏览器如 Chrome 会抛出 QUOTA_EXCEEDED_ERR 异常。所以虽然 DOM Storage 提供的空间比 cookie 要大很多,但在使用需要注意限制。

图 2. Chrome 浏览器抛出异常

安全性

一般不要在客户端存储敏感的信息,使用 localStorage、globalStorage 等在客户端存储的信息都非常容易暴露。应该在完成数据存储后使用 clear 或者 removeItem 方法清除保存在 Storage 对象中的数据。

存储事件驱动

如果想在存储成功或修改存储的值时执行一些操作,可以用 DOM Storage 接口提供的事件。可以使用如下方法注册事件:

window.addEventListener(“storage”, handleStorageEvent, false);

存储事件接口定义

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:发生改变的键。

oldValue:键改变之前的值。

newValue:键改变之后的值。

url:触发存储事件的页面 url。

在清单 6 中注册完存储事件后,当 sessionStorage 或者 localStorage 对象的值发生改变时,会触发 handleStorageEvent 函数,在页面显示发生改变的键和改变之前与之后的值。

清单 6. 添加存储事件
 // 显示存储事件的相关内容
 function handleStorageEvent(e) {
    document.write(“key” + e.key + “oldValue” + e.oldValue + “newValue” + e.newValue);
 }
 // 添加存储事件监听
 window.addEventListener(“storage”, handleStorageEvent, false);

回页首

使用 Dojo 实现之前用户注册的功能

Dojo 是一个 JavaScript 实现的开源工具包,很大程度上屏蔽了浏览器之间的差异性。Dojo 扩展库 (dojox) 是 Dojo 在其基本库、核心库和 Dijit 库的基础上提供的一个非常丰富的组件仓库。本文用到的 dojox.storage 模块能够将数据保存在本地存储中,实现和之前 DOM Storage 一样的功能。

由于一些老版本浏览器不支持 HTML5,我们还可以用 Dojo 来实现之前用户注册的功能。相对于 HTML5 的 DOM Storage 接口,Dojo 的 dojox.storage.Provider 接口提供的方法更多。这里我们列出几个常用的方法。

get(key, namespace):返回指定键对应的值。

put(key, value, resultsHandler, namespace):存入一个键值对。

remove(key, namespace):删除指定的键值对。

clear(namespace):删除对象中的所有键值对。

现在对第一个表单的 JavaScript 代码做部分修改,并在页面中引入 dojox.storage 模块。这样,程序在不支持 HTML5 的浏览器中能够通过调用 Dojo 提供的方法正常运行。dojo.require("dojox.storage") 表示引入 dojox.storage 功能模块。然后通过 dojox.storage.manager.isInitialized() 查看 dojox.storage.manager 是否已经初始化,如果没有的话,则需要等待其初始化完成之后,再进行存储操作。

清单 7. 经过修改后的第一个表单页面的部分代码
 <script type="text/javascript">
 dojo.require("dojox.storage");
 // 当退回到第一个页面时,从 Storage 中得到用户之前输入的值并显示在页面,方便修改
 // 这里先进行 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;
        }
    }
 } 

 // 保存数据
 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>
清单 8. 经过修改后的第二个表单页面的部分代码
 <script type="text/javascript">
 dojo.require("dojox.storage");
 // 将保存在 sessionStorage 中的数据赋给表单的隐藏属性
 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>

结束语

HTML5 中引入了 DOM Storage 机制用于存储键值对,它的设计目的是提供大规模、易用的存储功能,并且程序员可以通过调用标准的接口,简单地访问存储的数据。目前,许多新版本的浏览器都支持 DOM Storage 功能。当老版本的浏览器不支持 HTML5 提供的 DOM Storage 机制时,可以考虑用 Dojo 来实现相同的功能。

原文:http://www.ibm.com/developerworks/cn/web/1107_gaoly_html5storage/

浅谈 HTML5 的 DOM Storage 机制 (转)

时间: 2024-10-07 20:34:04

浅谈 HTML5 的 DOM Storage 机制 (转)的相关文章

浅谈HTML5单页面架构(二)——backbone + requirejs + zepto + underscore

本文转载自:http://www.cnblogs.com/kenkofox/p/4648472.html 上一篇<浅谈HTML5单页面架构(一)——requirejs + angular + angular-route>探讨了angular+requirejs的一个简单架构,这一篇继续来看看backbone如何跟requirejs结合. 相同地,项目架构好与坏不是说用了多少牛逼的框架,而是怎么合理利用框架,让项目开发更流畅,代码更容易管理.那么带着这个目的,我们来继续探讨backbone. 首

AngularJS进阶(二十五)requirejs + angular + angular-route 浅谈HTML5单页面架构

requirejs + angular + angular-route 浅谈HTML5单页面架构 众所周知,现在移动Webapp越来越多,例如天猫.京东.国美这些都是很好的例子.而在Webapp中,又要数单页面架构体验最好,更像原生app.简单来说,单页面App不需要频繁切换网页,可以局部刷新,整个加载流畅度会好很多. 废话就不多说了,直接到正题吧,浅谈一下我自己理解的几种单页面架构: 1.requirejs+angular+angular-route(+zepto) 最后这个zepto可有可无

浅谈Linux中的信号机制(二)

首先谢谢 @小尧弟 这位朋友对我昨天夜里写的一篇<浅谈Linux中的信号机制(一)>的指正,之前的题目我用的“浅析”一词,给人一种要剖析内核的感觉.本人自知功力不够,尚且不能对着Linux内核源码评头论足.以后的路还很长,我还是一步一个脚印的慢慢走着吧,Linux内核这座山,我才刚刚抵达山脚下. 好了,言归正传,我接着昨天写下去.如有错误还请各位看官指正,先此谢过. 上篇末尾,我们看到了这样的现象:send进程总共发送了500次SIGINT信号给rcv进程,但是实际过程中rcv只接受/处理了1

浅谈HTML5单页面架构(一)——requirejs + angular + angular-route

本文转载自:http://www.cnblogs.com/kenkofox/p/4643760.html 心血来潮,打算结合实际开发的经验,浅谈一下HTML5单页面App或网页的架构. 众所周知,现在移动Webapp越来越多,例如天猫.京东.国美这些都是很好的例子.而在Webapp中,又要数单页面架构体验最好,更像原生app.简单来说,单页面App不需要频繁切换网页,可以局部刷新,整个加载流畅度会好很多. 废话就不多说了,直接到正题吧,浅谈一下我自己理解的几种单页面架构: 1.requirejs

浅谈HTML5 WebSocket的机制

回顾上一章 在上一章<为什么我们需要HTML5 WebSocket>中,我简单的介绍了下WebSocket的前世今生.相信大家已对WebSocket有了初步的了解.那么今天我们继续深入学习WebSocket的机制. WebSocket机制 我们知道WebSocket是HTML5一种新的协议.它实现了浏览器与服务器全双工通信(不知道的可以看下全双工通信RS-422标准),能更好的节省服务器资源和带宽并达到实时通讯,它建立在TCP之上,同HTTP一样通过TCP来传输数据,但是它和HTTP最大不同是

浅谈HTML5单页面架构(三)—— 回归本真:自定义路由 + requirejs + zepto + underscore

本文转载自:http://www.cnblogs.com/kenkofox/p/4650310.html 不过,这一篇,我想进一步探讨一下这两个框架的优缺点,另外,再进一步,抛开这两个框架,回到本真,自己搞个简单的路由一样可以实现单页面. 这个对于刚做前端开发的新同学来说就最好不过了,如果一来到岗位就一大堆angular.backbone.requirejs,看资料都看一两周.其实大家最熟悉的东西还是那个美元$,用美元能解决的问题,就不要麻烦到angular.backbone大爷了. 事先说明,

由莫名其妙的错误开始---浅谈jquery的dom节点创建

有一个字符串是这样的:var s = '<html lang="en" class="js no-touch discourse-no-touch">'+ '<head><meta name="csrf-token" content="Oul7WqVh4FBVse2yGeY8ZkqoN5/9/2ImxohJvUYEJYc="/></head><body></bo

浅谈js的事件冒泡机制

很多人都听说过,js的事件冒泡机制,其实,这个说法还是比较生动形象的,就是一个水泡在水底下,冒泡到水面的过程. 那js的事件冒泡机制呢,就是一个DOM树,一级一级向上冒的过程,最终是到document这个根节点这里. 这里就有一个小细节,dom点击后,它所有的父级都会触发事件,每一级绑定的事件都会触发,这样是耗费事件和性能进行处理的,但是,如果我们把事件都绑定到document根节点,这样只需要在最后一级的时候,区别是那个dom触发的就行了,这个也叫事件委托. 其实这个场景适用于,子节点和众多父

浅谈Qt事件的路由机制:鼠标事件

请注意,本文是探讨文章而不是教程,是根据实验和分析得出的结果,可能是错的,因此欢迎别人来探讨和纠正. 这几天对于Qt的事件较为好奇,平时并不怎么常用,一般都是用信号,对于事件的处理,一般都是需要响应键盘按键事件的时候,也用得毫无问题,因此也没怎么注意过,翻了下一般qt的教材<精通Qt4编程(第二版)>,里面12.1是这么说的. 当用户按下一个鼠标键时,这个事件首先被发给当前拥有焦点的窗口部件. 看到这里,我第一反应是,真的是这样吗,我表示十分地好奇,于是就赶忙试验了一下.代码比较简单,没有注释