65.9K
CodeProject 正在变化。 阅读更多。
Home

HTML5 和 CSS3 第 10 部分:Web 存储的神奇功能

starIconstarIconstarIconstarIconstarIcon

5.00/5 (6投票s)

2014年5月3日

CPOL

13分钟阅读

viewsIcon

15503

了解如何轻松地在网站的客户端存储数据。

引言

在网站的客户端存储数据可能不是您以前考虑过的事情。如果您考虑过,您可能查看了 cookie 然后就放弃了。在 HTML5 中,您可以使用客户端数据存储做更多的事情!我将引导您了解客户端存储的一些优点,然后深入探讨存储和检索数据的相对简单的过程。我们将探讨您拥有的不同选项、存储数据的安全性以及如何将 Web Storage 提升到新的水平。

什么是 Web Storage

Web Storage 是 HTML5 中的一种机制,允许我们在客户端以键/值对的形式存储字符串数据。例如,您可以使用“FirstName”键和“Sue”值将一个人的名字存储起来。如果尚不存在名为“FirstName”的键,它将被创建。如果已存在名为“FirstName”的键,它将被覆盖,恕不另行通知。然而,这通常不是问题,因为 Web Storage 在存储数据的方式上非常具体。

Web Storage 中的数据存储是沙盒化的。这意味着站点只能访问自己的沙盒。它们无法访问任何其他站点的存储。站点及其沙盒由以下内容定义:

  • 域名(例如 stackoverflow.com)
  • 子域名(例如 careers.stackoverflow.com)
  • 协议(例如 http 或 https)
  • 浏览器(例如 Chrome 或 FireFox)
  • 会话类型(例如正常或隐私浏览)

要使网站能够访问 Web Storage 数据,以上所有项目都必须相同。会话存储会进一步细分到同一选项卡,这意味着在浏览器两个不同选项卡中打开的同一站点无法访问相同的 sessionStorage 沙盒。它们各自会获得自己的沙盒。

Web Storage 还分为两种存储类型:本地存储(Local Storage)和会话存储(Session Storage)。本地存储会“永远”保存在计算机上。实际上,它只保留到用户清除存储(清除缓存)为止。会话存储仅在会话持续期间有效。这意味着当您关闭网页时,存储就会消失。但请注意,情况可能并非总是如此。例如,如果浏览器崩溃,它可能会恢复会话和会话数据。此外,一些浏览器允许重新打开已关闭的选项卡并保留其会话数据。

关于 Web Storage 的另一个重要信息是它容量很大。我们可以在任何一个沙盒中存储最多 5MB 的数据。某些浏览器可能会进一步扩展此限制,但 5MB 是推荐的大小。由于我们存储的是字符串数据,这是一个巨大的空间,特别是与 cookie 的 4KB 限制相比。同样重要的是要注意,与 cookie 不同,Web Storage 数据不会自动传输到服务器。由于我们可以存储最多 5MB 的数据,这是一件好事。

最后,在使用 Web Storage 时,您需要意识到它不是安全的存储。其他站点无法访问数据,但这并不意味着数据受到保护。可以使用基本的 Web 工具在客户端读取 Web Storage 中的数据。这意味着您永远不应在未先加密的情况下将敏感数据存储在 Web Storage 中。

何时使用 Web Storage

在客户端存储数据并不是我们每天都会使用的事情。但是,在许多情况下,Web Storage 都非常有用。让我们来看几个有效使用 Web Storage 的例子:

长调查问卷

一遍又一遍地填写问卷会很累人且令人沮丧。用户倾向于离开页面。也许他们不小心关闭了浏览器。如果他们稍后回到调查页面,他们可能不会完成,因为所有工作都丢失了。如果您有本地存储,您可以恢复他们已回答的所有问题,并允许他们从上次中断的地方继续。您甚至可以将此作为调查的一项功能:花费您需要的时间,稍后再回来继续——您的工作将仍然在这里。

不经常更改的数据

如果您的页面是数据驱动的,但数据不经常更改,您可以将数据存储在本地存储中,从而减少对数据库的调用。例如,假设您有一个下拉框,其中列出了您的公司提供服务的国家/地区。该列表可以存储在客户端。这样,您就不需要每次都从数据库加载它。您可以存储一个告诉您何时更新列表的变量,或者您可以在初始页面加载时传递它。如果您的重复访问者比例很高,这将大大减少数据库流量。

站点设置

您可以允许访问者自定义您的网站(主题、重要链接、布局等)。您可以将此自定义数据存储在 Web Storage 中。这样,每次他们访问您的网站时,都会获得他们想要的样式。您可以根据访问者与网站的关系,使您的网站非常不同。亚马逊就经常这样做,利用服务器端和客户端信息的组合来确定主页上显示哪些产品。您也可以使用 Web Storage 来做同样的事情。

表单提交备份

表单数据通常对网站所有者来说很重要。您真的不想丢失用户愿意提供给您的信息。但是,当用户填写您的表单但网站中断(即使只有一秒钟)或提交出现故障时会发生什么?所有数据都会丢失。最好的情况是用户再次填写所有数据并重新提交。然而,这可能只占用户的一小部分。更糟糕的是,如果表单中有一个字段要求填写段落形式的反馈,用户第二次提交数据时可能会包含较少的细节,因为他们不想再次输入所有这些信息。这就是本地存储或会话存储可以发挥巨大作用的地方。您可以随着用户在字段之间移动来存储数据。然后,如果提交失败,您可以重试提交,或者如果您使用了本地存储,可以请用户稍后回来提交数据。

离线/缓存数据

如果您依赖的数据服务中断,您可以使用上次请求数据时存储在 Web Storage 中的数据备份。这可能不适用于所有数据,但可能比什么都没有好。它也可能减少对 Web 服务的请求。例如,如果用户搜索同一个词多次,您可以返回缓存数据,而不是再次访问 Web 服务。

使用 Web Storage

为简化起见,从现在开始,我将把会话存储和本地存储统称为存储或 Web Storage。在我的示例中,我将向您展示如何使用每种存储类型完成相同的任务,并指出出现的任何差异。

我们先来看看如何使用 Web Storage 存储数据(每行代表一种方法——任何一种都可以)

// Local Storage
localStorage.setItem('key', 'value');
localStorage.key = 'value'
localStorage['key'] = 'value'

// Session Storage
sessionStorage.setItem('key', 'value');
sessionStorage.key = 'value'
sessionStorage['key'] = 'value'

现在让我们看看如何检索数据

// Local Storage
var value = localStorage.getItem('key');
var value = localStorage.key;
var value = localStorage['key'];

// Session Storage
var value = sessionStorage.setItem('key');
var value = sessionStorage.key;
var value = sessionStorage['key'];

最棒的是,您不必记住用于保存数据的特定方法即可检索它。这三种方法中的任何一种都可以检索使用任何一种方法存储的数据。这三种方法之间存在一些速度差异,但这是一个不断变化的目标。最终取决于您认为最适合您的工作流程。

如果您想从存储中删除一个项目,您可以这样做:

// Local Storage
localStorage.removeItem('key');
delete localStorage.key;
delete localStorage['key'];

// Session Storage
sessionStorage.removeItem('key');
delete sessionStorage.key;
delete sessionStorage['key'];

这些方法都会从存储中删除该项目,因此如果您尝试访问该键,您将得到“undefined”。如果您决定一次性清除所有存储,可以使用 clear 方法,如下所示:

// Local Storage
localStorage.clear();

// Session Storage
sessionStorage.clear();

要检查存储中有多少对象,您可以运行以下命令:

// Local Storage
var itemsCount = localStorage.length;

// Session Storage
var itemsCount = sessionStorage.length;

这将为您提供一个整数值,指示您选择的存储机制当前存储了多少项。

Web Storage 的限制

Web Storage 的规范规定,每个存储沙盒应保留 5MB。某些浏览器可能允许额外的空间,或者允许用户指定每个沙盒获得多少空间,但这并非网站可以更改或影响的内容。由于存在限制,我们必须计划达到限制的情况。

当您超出特定沙盒的存储限制时,您将收到一个 QUOTA_EXCEEDED_ERR 错误。您应该在代码中留意此错误,以确保您能够处理这种情况(如果发生)。为此,您可以使用 try/catch,如下所示:

// Local Storage
try {
   localStorage.key = value;
} catch (e) {
   if (e.name === 'QUOTA_EXCEEDED_ERR') {
      // The quota has been exceeded. Maybe put a clear() here 
      // or make some room in some other way
   } else {
      // A different error occurred
   }
}

// Session Storage
try {
   sessionStorage.key = value;
} catch (e) {
   if (e.name === 'QUOTA_EXCEEDED_ERR') {
      // The quota has been exceeded. Maybe put a clear() here 
      // or make some room in some other way
   } else {
      // A different error occurred
   }
}

这里的技巧是,并非所有浏览器都会实现正确的错误消息。将 set storage 行包装在 try/catch 中(如我们此处所示)可能是最安全的做法,然后只假设错误意味着存储已满。

由于存储已满时可能会发生错误,因此了解我们何时接近限制会很有用。在 Internet Explorer 中,存储对象上有一个“remainingSpace”属性,它以字节为单位给出剩余空间。不幸的是,IE 是唯一实现此功能的浏览器。除非您希望您的网站仅针对 IE 开发,否则不应使用此属性。相反,找出剩余空间的最佳方法是找出已使用的空间,然后假定存储限制为 5MB。例如,这里有一个函数,它将告诉您到目前为止在存储中使用了多少字节:

// Local Storage
function localStorageUsed() {
    var output = 0;
    for(var x in localStorage){
        output += (localStorage[x].length * 2);
    }
    return output;
};

// Session Storage
function sessionStorageUsed() {
    var output = 0;
    for(var x in sessionStorage){
        output += (sessionStorage[x].length * 2);
    }
    return output;
};

您可以通过从五百万中减去该数字来计算剩余空间。尽管 5MB 技术上是 530 万字节以上,但至少 Internet Explorer 使用约 500 万字节作为存储空间。最好低估您的空间需求而不是高估。

Web Storage 的最后一个主要限制是其实现。并非所有浏览器都实现了 Web Storage。最值得注意的是,Internet Explorer 6 和 7 完全不实现它,而 IE 8 的实现有限。要验证客户端浏览器是否支持 Web Storage,您可以使用以下代码:

// Verifies both Local Storage and Session Storage
if(typeof(Storage) !== "undefined") {
   //Both localStorage and sessionStorage are supported
} else {
   // Web storage is not supported
}

此测试的唯一问题是,如果有人定义了一个 Storage 对象。在这种情况下,测试将通过,而没有真正验证 Web Storage。Modernizr 进一步尝试绕过一些边缘情况,它将 setItem 函数调用和 removeItem 函数调用包装在 localStorage 的 try/catch 中。如果任一命令失败,则表示浏览器不支持 Web Storage。如果两者都成功,则表示浏览器支持 Web Storage。

Web Storage 事件

当您在 Web Storage 中存储数据时,会触发一个事件。该事件指示受影响的键、旧值以及现在的新值。此事件可用于观察其他页面正在做什么。这带来了该事件最大的限制:它不会在触发事件的页面上触发。这意味着,出于实际目的,此事件仅限于本地存储。

要捕获此事件,首先需要像这样向事件注册一个监听器:

window.addEventListener('storage', handleStorageEvent, false);

接下来,您需要创建 handleStorageEvent 函数(或您称呼它的任何名称),如下所示:

function handleStorageEvent(e) {
   // Use e.key for the key that was changed
   // Use e.oldValue to get the old value of the key
   // Use e.newValue to get the new value of the key
}

现在,每当同一沙盒中的另一个页面(通常是另一个选项卡)更改数据时,此事件都会触发。

高级 Web Storage

存储名字、姓氏甚至一段文本都很棒,但有时这还不够。虽然 Web Storage 确实有只存储字符串的限制,但有一些方法可以充分利用我们存储的字符串。让我们来看几个示例,说明如何充分利用 Web Storage。

数据集

在 JavaScript 中,我们通常处理包含多个属性的对象。例如,一个对象可能包含名字、姓氏和邮政编码。这些数据可能用于绑定到我们表单中的输入。在 JavaScript 中,这可能是一个示例:

var model = {
   firstName: 'Tim',
   lastName: 'Corey',
   zipCode: '18411'
};

假设您想将此数据存储在 Web Storage 中。您可以像这样将每个属性存储在自己的键/值对中:

// Local Storage
localStorage.firstName = model.firstName;
localStorage.lastName = model.lastName;
localStorage.zipCode = model.zipCode;

// Session Storage
sessionStorage.firstName = model.firstName;
sessionStorage.lastName = model.lastName;
sessionStorage.zipCode = model.zipCode;

这很快就会变得笨拙。即使是这个例子也很丑陋。为了简化,我们可以将整个 JavaScript 对象存储在一个键/值对中。关键是使用 JSON.stringify() 方法先将对象转换为字符串:

// Local Storage
localStorage.model = JSON.stringify(model);

// Session Storage
sessionStorage.model = JSON.stringify(model);

稍后,当您想重新填充模型时,需要使用 JSON.parse() 方法,如下所示:

// Local Storage
model = JSON.parse(localStorage.model);

// Session Storage
model = JSON.parse(sessionStorage.model);

这里很棒的是,您的模型有多少属性并不重要。您甚至不需要知道所有属性的名称。您只需保存整个模型即可。这使您的代码维护更少。它还可以处理模型中的任何数量的属性,而无需了解它们。您可以编写一些更复杂的代码来查看附加到您模型的每个属性(未继承的属性),然后单独存储每个属性(希望不会干扰任何当前使用的键),但这似乎工作量很大。这个解决方案干净得多。

图像和 Canvas

虽然有更好的缓存图像的方法(请参阅 App Cache),但有时您可能想在 Web Storage 中存储一个 Canvas 对象。例如,如果您允许用户在 Canvas 上创建图像,您可能希望保存该图像以备将来使用。要做到这一点,您需要使用 toDataURL() 方法导出 Canvas JavaScript 对象,如下所示:

// JavaScript setup
var canvas = document.getElementById('canvasId');
var context = canvas.getContext('2d');

// Do any work on the canvas here

// Export the canvas object to string
var canvasString = canvas.toDataURL();

// Local Storage
localStorage.canvasId = canvasString;

// Session Storage
sessionStorage.canvasId = canvasString;

稍后访问该 Canvas,您需要这样做:

// Local Storage
var canvasString = localStorage.canvasString;

// Session Storage
var canvasString = sessionStorage.canvasString;

// JavaScript setup
var canvas = document.getElementById('canvasId');
var context = canvas.getContext('2d');

// Sets up a new image that will use the canvas text
// as the image source
var canvasImg = new Image();

// Fires an event once the image loads that actually puts the image
// onto the canvas
canvasImg.onload = function () {
   context.drawImage(this, 0, 0);
};

// Assigns the text to the source of the image
canvasImg.src = canvasString;

这有点复杂,而且绝对是一个边缘情况。然而,在少数需要这样做的情况下,Web Storage 可以胜任。

结论

Web Storage 并非适用于所有网站,但在某些情况下它非常强大且易于使用。借助它,我们可以节省数据库调用,改善用户体验,并优雅地从崩溃中恢复。在这篇短文中,我们介绍了 Web Storage 是什么,如何使用它,以及如何充分发挥其功能。现在,您所要做的就是将这项技术应用到您的网站中。

© . All rights reserved.