转载 原文

在此两部分组成的系列中,我们将来看看HTML5 规范中最棒而且最有趣的特性之一的Web Storage。我们将看看Web Storage 和 Cookies的历史,并从考虑以下几点:

*Cookies 怎么样又为何被使用

*Web Storage 的长处与限制

*当前浏览器对Web Storage 与 Cookies的支持度

*未来在web app中管理事务状态

*如何利用Web Storage构建离线app

由于该系列是需要动手实践的,我们将一起通过简单的程序来利用Web Storage。我们将利用它创建在线或离线都可用的应用,这样你就可以在四处使用它了。

Cookies 历史简短介绍

Cookies 于1994年作为 Mosaic Netscape 浏览器beta 0.9 版的一部分被引入,Netscape的客户MCI想要找到一种方式来保持状态,但不是在服务器端。Cookies作为技术解决手段被选中了。

Cookies在形式和结构上都很简单,是一种有效的解决手段,它允许在用户计算机上存储小段文本信息。这些存储的信息能够使得网站提供更加个性化的体验,例如:

*保持登录

*保持上次查看的页面

*浏览计数

*广告追踪

*购物车的状态保持

Cookies是如何起效的?

当浏览器从web的回应到页面请求中接收到一个 Set-Cookie 头部时Cookies便创建了:

1
2
3
HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: page_loaded=25; Expires=Wed, 09 Jun 2021 10:18:14 GMT

浏览器接收到表明回应成功的 HTTP 200 代码,以及回应的内容类型。同事也接收到了 Set-Cookie头部,并创建了一个一下结构的cookie:

除非在Wed, 09 Jun 2021 10:18:14前刷新,否则cookie将在这以后无效并被浏览器移除。如果它没有被终止,在将来所有的该网站的请求中都将携带类似的信息头部:

1
2
3
GET /spec.html HTTP/1.1
Host: www.example.org
Cookie: page_loaded=25;

在设置cookie的时候有很多的选项可以指定。这些包括域名、路径和是否安全或者HttpOnly。

Cookies趋势下滑

先不管那些很明显的优势和简洁性,cookie一直在潜在隐私和安全影响方面有一个坏名声。他们很容易受到安全问题攻击影响,例如关键攻击载体的CSRF(Cross Site Request Forgery),XSS(Cross Site Scripting Attacks) 以及 Session Hijacking 。一个用功且专业的开发者也许不会把很多安全细节信息放在cookie中,或者实现一系列的方法来减轻可能的这些形式的攻击。

Cookies 不应该被看做是种自动的威胁。没有它们,很多我们期待的web个性化都将变得不可能。

HTML5 存储简介

HTML5引入了Web Storage作为Cookies的替代,这种存储有两种类型:local 和 session:

你可以根据你程序的重点选择最适合你目标的存储方式。正如cookies一样,HTML5存储有自己的优势以及缺陷。

Web Storage 的缺陷

*数据以简单字符串存储;操作所需的存储不同类型的对象,例如booleans,objects,ints和floats。

*默认5MB存储限制;如果需要时可有用户允许获取更多存储。

*可被用户或者系统管理员禁用

*复杂数据集合存储可能很慢

Web Storage 的长处

*App 可以再在线或离线情况下使用

*拥有简单好用易学的API

*能够对浏览器使用事件钩子,例如offline,online,storage change

*比cookies更便于管理,没有额外的的请求头部数据

*提供更大的空间以存贮日益剧增的复杂数据

Web Storage API

Web Storage API 简单易学,它只包含四个方法:

这些方法为我们提供了所有使用本地存储所需要的。正如编码中的所有东西一样,我们可以创建类和对象或者使用第三方的库来是的交互更加精致。

要使得使用Web Storage很简单,你可以使用第三方库之一例如Modernizr 和 jStorage

浏览器事件

HTML5 暴露了很多开发者可以使用的很多事件,在此文中,我们将看到 offline 和 online。

安全性

HTML5 可能很闪耀并且是崭新的,但是这不意味着比以前更加的安全。记住需要为Cookies做的那些安全考虑。在重要的是,不要将敏感信息存放在Web Storage里!我知道这无需多言,但是值得重复提醒。

正如Cookies一样,用户可以查看这些信息并将其删除,而且这些信息仍然是跨站脚本攻击的目标。所以使用它是要多费点心思。

Web Storage的一个关键的改进就是提供local 和 session的区别存储。Session 存储当窗口关闭或者标签关闭时将会移除所有存储信息。Local 存储将在标签间以及窗口绘画保持信息。通过在标签或者窗口关闭而将数据的自动移除的方式,我们能够获得一个更加安全的存储环境,我强调一下,是更安全,而不是安全。

在使用时使其更加简单能够获得附加的好处,如果我们清楚我们只需要短时间的存储,那么我们可以将其放在交互式时也许会被自动清理的部分。但是不要因此养成懒惰草率的编码习惯。

浏览器支持

虽然本地存储是一个相当不错的进展,但是这不意味着所有的浏览器都同样地支持它。以下表格展示了主流浏览器哪些版本开始支持它。

现如今所有的现代浏览器都支持本地存储,但是你需要考虑旧的版本。

为何HTML5存储将是事务状态的未来?

根据我们已经提到的一些原因,我相信HTML5存储将是事务状态的未来,这主要是由无法抑制的移动互联网的崛起驱动的(纵观 iOS,Android 和 Windows Phone 7智能手机可知)。

正如你所见到的下面的表格,智能手机数量的全球销售数额在2011年的Q4停滞不前。

通过这些数字的判断,用不了多久这些设备将最终取代桌面互联网的使用。可问题是,我们无法保证总是联网的。间歇性地信号覆盖太常见了。

无论我们是在地铁网络中,或者乘飞机环行世界或者在更多的偏远的国家地区,在那里数据漫游变得效率更低,在很多条件下我们都没法保证持久的网络连接。

第二,不想很多情况下的iOS 5 的升级以及潜在的 Windows Phone,Android手机耗电可是出了名的。所以需要做的越少那么需要传输和接受的数据就越少,这将直接让终端用户体会到最为有效和反应快速的app。

本地存储能够让我们以多种方式使用,如下:

*不必在每次请求时都带上Cookies信息

*可以在无数据连接的情况下使用,并且能够在与服务器连接恢复后合并离线状态

*可以存储和操作更多的本地信息

一个简单的应用

现在我们已经大致了解和介绍了一些关于什么是web存储以及它能干什么,现在让我们着手做一个应用来用用它。该应用是一个在线或者离线都可用的简单用户列表。

该应用从一段 PHP 脚本中检索一个用户列表,get-records.php.它将这些信息保存在本地存储中,然后通过简单的jQuery函数调用将这些数据呈现在表格中,所以用户便可以看到当前可用的用户列表。

一个用户可以添加一个3bits的新用户信息:姓,名以及email地址。当表单被提交后,这些信息将被发送到add-record.php。SQLite 数据库将被更新然后将为这些信息构建一个请求,重新获取记录列表并重新显示用户信息。

该应用不是最有效的方式,但是它能够在浏览器在线或者离线时进行跟踪,并是的表格在任何一种方式下都能有效。如果该应用离线,表单数据将会被存储在第二个记录里,并随时准备在浏览器恢复到在线时通过add-record.php存储到数据库,并清除数据和重新获取页面数据。

看一下下面的代码,你便会知道是怎样处理的:

1
2
3
4
5
<script type="text/javascript">
  
    /* PHP scripts to call */
    var getRecords = 'get-records.php';
    var addRecords = 'add-record.php';

这是一小段我们用来交互的PHP代码段。

1
2
3
4
5
6
7
function supports_html5_storage() {
  try {
    return 'localStorage' in window && window['localStorage'] !== null;
  } catch (e) {
    return false;
  }
}

上面的代码能够快速的检测HTML5本地存储是否可用,如果返回false,那么就不可用了。

1
2
3
4
5
6
7
8
9
10
11
function clearPrimaryCache()
{
    localStorage.removeItem('records.user.hasRecords');
    localStorage.removeItem('records.user.records');
}
  
function clearOfflineCache()
{
    localStorage.removeItem('records.user.local.records');
    localStorage.removeItem('records.offline.status');
}

这里有两个我们主要用到的缓存:一个用于在线数据对用户的显示,另外一个用于离线数据。分开清除每一个都很简单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function getRecords()
{
    if (!supports_html5_storage()) {
        return false;
    }
  
    $(function()
    {
        $(document).ready(function()
        {
            $.getJSON(getRecords, function(data) {
                clearPrimaryCache();
                var records = [];
                records.push(data.records);
                if (records.length !== 0) {
                    localStorage.setItem('records.user.records', JSON.stringify(records));
                    localStorage.setItem('records.user.hasRecords', JSON.stringify(true));
                }
            });
            return false;
        });
    });
}

然后我们通过 get-records.php 检索数据记录,并清除原来的缓存,缓存检索的记录,然后设置一个变量表明我们的可用记录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function showCurrentRecords()
    {
        if (!supports_html5_storage()) {
            return false;
        }
  
        var hasRecords = JSON.parse(localStorage.getItem("records.user.hasRecords"));
        if (!hasRecords) {
            $('#current-records > tbody').append('
  
<tr>
<td colspan="4">No Records available</td>
</tr>
  
');
        } else {
            var userRecords = JSON.parse(localStorage.getItem("records.user.records"));
            $.each(userRecords[0], function(index, row) {
                $('#current-records > tbody').append(
                    '
  
<tr>
<td>' + (index + 1) + '</td>
<td>' + row.firstname + '</td>
<td>' + row.lastname + '</td>
<td>' + row.emailaddress + '</td>
</tr>
  
'
                );
            });
        }
    }

上面的代码将当前可用的记录从缓存中取出并呈现到页面上。我们检查记录是否可用,如果可用则构建一个简单的HTML表格。

1
2
3
function clearRecords(){
    $("#current-records > tbody > tr").remove();
}

现在我们清除HTML 表格中的存在的记录。

1
2
3
4
5
function reloadRecords(){
    getRecords();
    clearRecords();
    showCurrentRecords();
}

这是一个简单的工具方法,它使我们能够从PHP脚本中检索数据,移除HTML表单中已存在的记录,重建查询到的记录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function goOnline()
{
    // persist the local records to the remote and clear the buffer
    var userRecords = JSON.parse(localStorage.getItem("records.user.local.records"));
  
    // process any records retrieved
    if (userRecords.length >= 1) {
        $.each(userRecords, function(index, row) {
            var dataString = 'firstname='+ row.firstname + '&lastname=' + row.lastname + '&emailaddress=' + row.emailaddress;
            $.ajax({
                type: "POST",
                url: "add-record.php",
                data: dataString,
                success: function() {
                    reloadRecords()
                }
            });
        });
  
        // clear out the offline cache after processing
        clearOfflineCache();
  
        // reload the records so that the user sees them
        reloadRecords();
    }
  
    // set the indicator flag to show that we're now online
    localStorage.setItem('records.offline.status', JSON.stringify('online'));
}

goOnline 使得检测到在线状态时令应用在线。它检索任何被缓存的记录并通过调用 add-records.php脚本存储他们,清除离线缓存并调用reloadRecords函数,它将重新渲染数据让用户看到数据变化和更新。它最终将把状态指示器设置为online。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function goOffline()
    {
        localStorage.setItem('records.offline.status', JSON.stringify('offline'));
    }
  
goOffline takes the application offline by setting the status indicator appropriately.
    function isOffline()
    {
        var offlineStatus = JSON.parse(localStorage.getItem("records.offline.status"));
        if (offlineStatus) {
            return true;
        }
        return false;
    }

isOffline 检测离线指示器 如果浏览器离线则返回true,如果在线则返回false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function persistRemotely()
{
    var firstname = $("input#firstname").val();
    var lastname = $("input#lastname").val();
    var emailaddress = $("input#emailaddress").val();
    var dataString = 'firstname='+ firstname + '&lastname=' + lastname + '&emailaddress=' + emailaddress;
  
    // if online, submit to remote script
    $.ajax({
        type: "POST",
        url: "add-record.php",
        data: dataString,
        success: function() {
            reloadRecords()
        }
    });
    return false;
}

persistRemotely将保存从提交表单中获取的记录。它从姓,名以及email地址域中获取值,构建一个简单的数据查询字符串作为request的POST数据,并通过AJAX提交到add-records.php脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function persistToOfflineCache()
{
    var firstname = $("input#firstname").val();
    var lastname = $("input#lastname").val();
    var emailaddress = $("input#emailaddress").val();
  
    // retrieve the locally persisted records
    var records = JSON.parse(localStorage.getItem("records.user.local.records"));
  
    // parse the string to a json array
    var userRecord = {'firstname':firstname,'lastname':lastname,'emailaddress':emailaddress};
  
    if (records != null) {
        // store the record in the local records
        records.push(userRecord);
    } else {
        var records = [];
        // store the record in the local records
        records.push(userRecord);
    }
  
    // write the information back to local storage
    localStorage.setItem('records.user.local.records', JSON.stringify(records));
}

persistToOfflineCache缓存浏览器离线时的表单数据。跟persistRemotely一样,它将检索到的表单输入以JSON对象添加到离线缓存中,如果已存在记录,则把它添加进去,如果没有,则创建带有第一个记录的离线缓存。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$(document).ready(function() {
  
        // intercept the online event
        window.addEventListener("online", function() {
            goOnline();
        }, true);
  
        // intercept the offline event
        window.addEventListener("offline", function() {
            goOffline();
        }, true);
  
        // intercept the form submit event
        $('form').submit(function() {
            if (isOffline()) {
                persistToOfflineCache();
            } else {
                persistRemotely();
            }
            $('#recordsform').reset();
          return false;
        });
    });
  
    getRecords();
// ]]></script>

这是个应用测试工具,当页面完全加载后,我们侦听online 和 offline事件,并分别调用goOnline 和 goOffline。

根据当前浏览器状态我们拦截表单提交方法并调用persistToOfflineCache 或者 persistRemotely ,在其中一个被调用后重置表单防止标准表单反应的调用。

PHP 脚本

用来添加和存储记录的PHP脚本都很琐碎。正如你所看到的,这些记录都被存储在SQLite(3)数据库中。

add-records.php脚本查询POST 数据,让后使用准备好的语句插入记录到数据库中。get-records.php 从数据库中查询所有存在的记录并通过json编码输出。

add-record.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!--?php // Set default timezone date_default_timezone_set('UTC'); $dsn = "sqlite:databases/mydb.sq3"; try {     $dbh = new PDO($dsn);     // Set errormode to exceptions     $dbh--->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
    echo 'Connection failed: ' . $e->getMessage();
}
  
$userList = null;
  
// Select all data from file db messages table
$result = $dbh->query('SELECT * FROM users');
  
if (!empty($_POST['firstname']) && !empty($_POST['lastname']) && !empty($_POST['emailaddress'])) {
    $sql = "INSERT INTO users (firstname, lastname, emailaddress) VALUES (:firstname, :lastname, :emailaddress)";
    $q = $dbh->prepare($sql);
    $q->execute(array(
        ':firstname' => $_POST['firstname'],
        ':lastname' => $_POST['lastname'],
        ':emailaddress' => $_POST['emailaddress']
    ));
}

get-records.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!--?php // Set default timezone date_default_timezone_set('UTC'); $dsn = "sqlite:databases/mydb.sq3"; try {     $dbh = new PDO($dsn);     // Set errormode to exceptions     $dbh--->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
    echo 'Connection failed: ' . $e->getMessage();
}
  
$userList = null;
  
// Select all data from file db messages table
$result = $dbh->query('SELECT * FROM users');
foreach ($result as $row) {
    $userList['records'][] = array(
        "firstname" => $row['firstname'],
        "lastname" => $row['lastname'],
        "emailaddress" => $row['emailaddress']
    );
}
  
print json_encode($userList);

App 运行

没有截图的应用是不完整的,所以下面便是应用的截图了,继续了不起的Twitter Bootstrap 项目,一个简单的导航菜单下跟着的是表单,最后是当前的记录。

结论

我希望本文能够给你一个对HTML5强大而灵活的Web Storage的不错介绍,并让你一瞥它可能怎么被使用,并了解到在日益增长的移动互联网需求下为何它是如此重要。

HTML5 Web Storage -- 让Cookies看起来如此古老的更多相关文章

  1. HTML5 Web Storage使用实例

    很久没写文章了,忙加懒实在没办法,之前也看过关于Web Storage的文章,当时就觉得各各浏览器的支持跟上来还早着呢,像我们这样做门户网站的一时半会儿也用不上,毕竟用户群体鱼目混杂嘛,最近各各浏览器 ...

  2. HTML5 Web Storage

    Web Storage是HTML5 API提供一个新的重要的特性: 最新的Web Storage草案中提到,在web客户端可用html5 API,以Key-Value形式来进行数据持久存储: 目前主要 ...

  3. HTML5 Web Storage 特性

    原文地址: Using HTML5 Web Storage 原文日期: 2010年06月28日 翻译日期: 2013年08月12日 当下Web开发领域最火爆的词语当属 HTML5.HTML5标准的新特 ...

  4. HTML5 Web Storage概述

    Web Storage html5新增功能 可以在客户端本地保存数据 之前是使用Cookies在客户端保存注入用户名等简单用户信息,但永久数据存在几个问题 大小:cookies大小被限制在4KB 带宽 ...

  5. html5 Web Storage(localStorage(),sessionStorage())

    Web Storage包括了两种存储方式:sessionStorage和localStorage sessionStorage 是会话级别的存储,这些数据只有在同一个会话中的页面才能访问并且当会话结束 ...

  6. HTML5 WEB Storage - localStorage存储位置在哪

    localStorage作为客户端浏览器持久化存储方案 这个是浏览器隔离的,每个浏览器都会把localStorage存储在自己的UserData中,如chrome一般就是 C:\Users\你的计算机 ...

  7. HTML5本地存储 Web Storage

    Web Storage基本介绍 HTML5 定义了本地存储规范 Web Storage , 提供了两种存储类型 API  sessionStorage 和 localStorage,二者的差异主要是数 ...

  8. Where to Store your JWTs – Cookies vs HTML5 Web Storage--转

    原文地址:https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage Update 5/12/20 ...

  9. web 存储方式汇总:Cookies,Session, Web SQL; Web Storage(LocalStorage ,SessionStorage),IndexedDB,Application Cache,Cache Storage

    1 1 1 web 存储方式汇总: 旧的方式: Cookies; Session; Web SQL; 新的方式 HTML5 : Web Storage(LocalStorage ,SessionSto ...

随机推荐

  1. 51NOD 1623 完美消除 数位DP

    题目描述: 定义数的消除操作为选定[L,R,x],如果数的第L到第R位上的数字都大于等于x,并且这些数都相等,那么该操作是合法的(从低位到高位编号,个位是第一位,百位是第二位……),然后将这些位数上的 ...

  2. Codeforces Gym 100342J Problem J. Triatrip 求三元环的数量 bitset

    Problem J. Triatrip Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100342/at ...

  3. ACM-ICPC国际大学生程序设计竞赛北京赛区(2015)网络赛 B Mission Impossible 6

    #1228 : Mission Impossible 6 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 You must have seen the very famou ...

  4. 如何在 .Net Framework 4.0 项目上使用 OData?

    最新的 Microsoft ASP.NET Web API 2.1 OData 5.1.0 已只能在 .Net Framework 4.5 的安装了,如果要在 VS2010的 .Net Framewo ...

  5. 转 Web移动应用调试工具——Weinre

    如今人们也越来越习惯在手机上浏览网页,而在手机上这些针对桌面浏览器设计的网页经常惨不忍睹.Web应用开发者需要针对手机进行界面的重新设计,但是手机上并没有称心如意的调试工具(如Firebug.web ...

  6. NHibernate's inverse - what does it really mean?

    NHibernate's concept of 'inverse' in relationships is probably the most often discussed and misunder ...

  7. Java 读写图像

    Java中进行图像I/O(即读图片和写图片,不涉及到复杂图像处理)有三个方法:1. Java Image I/O API,支持常见图片,从Java 2 version 1.4.0开始就内置了.主页:h ...

  8. 【C语言】05-printf和scanf函数

    一.printf函数 这是在stdio.h中声明的一个函数,因此使用前必须加入#include <stdio.h>,使用它可以向标准输出设备(比如屏幕)输出数据 1.用法 1> pr ...

  9. rsync服务器的配置和使用

    yum install -y rsyncuseradd rsync -s /sbin/nologinmkdir /backupmkdir /backup1 chown rsync:rsync /bac ...

  10. 转:值得推荐的C/C++框架和库(真的很强大)

    目录(?)[+] 值得学习的C语言开源项目 - 1 Webbench - 2 Tinyhttpd - 3 cJSON - 4 CMockery - 5 Libev - 6 Memcached - 7 ...