HTML5的最有趣的功能之一是本地存储数据并且允许应用程序离线运行的功能。 共有三种不同的处理这些功能的API,如何选中其中之一取决于你希望对你将要本地存储的数据进行怎样处理:
- Web 存储:适用于具有 key/value对的基本本地存储
- 离线存储:利用一个 manifest文件来高速缓存所有文件以便离线使用
- Web 数据库:适用于关系型数据库存储
Web存储API
在用户的机器上进行本地存储的最基本的实现方法是利用web存储API。 该API使用 key/value 对来支持开发人员存储能够被 web 应用程序访问的基本信息和变量。 该功能的一个理想用例是用于存储那些在用户已经浏览完并且离开应用程序或已经关闭 web 浏览器之后需要永久保留的简单数据。 例如,保存游戏状态、保存导航位置或存储你希望在整个 web 应用程序中使用但你不希望使用 cookie 的一些特定信息(例如用户名称或姓名)。 类似的 API 还可以用于为个体会话存储数据。 这些数据将在用户浏览完离开应用程序或关闭浏览器之后自动清除。
当使用 web 存储 API 时,你需要记住下列事项:
- 该存储操作只能适用于相关的域(domain-wide),因此当使用web存储功能保存数据时,只有在该域上的其它网站才能访问这些数据。
- 存储数据的大小限制是大约5M字节。 5M字节的限制是由 W3C建议的,但该规范提供了实现细节的一些保留空间,因此,实际的字节大小取决于每个浏览器厂商。
核查浏览器支持功能
在使用web存储API之前需要做的第一件事情是核查用户的浏览器是否支持这一API:
function checkLocalStorageSupport() {
try {
return 'localStorage' in window && window['localStorage'] !== null;
}
catch (e) {
return false;
}
}
你也许在上面的代码片段中明显地看出,web存储使用一个名称为localStorage
的对象,它是一个windowclass对象。 上面的代码片段能够核查localStorage
实际上是否是一个基于 window的对象,以及它能否返回true,以便应用程序能够充分利用本地存储API。
添加和返回数据
从localStorage
对象中添加和返回数据与调用由本地存储规范实现的getter和 setter方法一样简单。 使用localStorage
可以存储任何类型的数据,但所有的数据必须以字符串的格式存储于相应的存储区域。 这意味着在将数据发送到localStorage
之前或在使用它们之后有必要对它们进行解析。 例如,为了存储一个 JavaScript 对象,必须使用JSON 并且在检索数据之前调用 stringify()
而在检索数据之后调用parse()
。 在从localStorage
对象中检索到数值变量之后,对它们进行解析也是如此。
在本范例中,已经建立一个单一的表单输入,这样,当用户点击Submit时,相应的数据将存储到本地高速缓存区域。 当加载页面时,如果数据已经存储,则页面将通过一个欢迎窗口显示已存储的信息。 下面是当用户点击 Submit时调用的函数:
function onClick() {
if (checkLocalStorageSupport) {
window.localStorage.setItem("name", document.getElementById("name").value);
}
}
该函数使用localStorage
对象中的 setItem
方法,然后使用相应表单中的值来填充存储高速缓存区。 当加载页面时,应用程序利用一个 onLoad
函数来核查相应的数据是否已经位于本地高速缓存区中并且将添加一个欢迎窗口。
function onLoad() {
if (checkLocalStorageSupport) {
var name = window.localStorage.getItem("name");
if (name != null) {
window.document.getElementById("divName").innerHTML = "Welcome back " + name;
}
}
}
清除数据
尽管用户能够使用浏览器在任何时间删除localStorage
数据,但为他们提供从应用程序自身删除数据的选项也是合理的。localStorage
提供一个正好能够实现这一目的 clear()
方法。 当用户在你的应用程序中点击一个复位按钮时,将触发下面代码。 如果你希望从存储区中移除一个特定条目,则你可以使用removeItem()
方法从本地存储区中删除一个单一key。
function onReset() {
if (checkLocalStorageSupport()) {
window.localStorage.clear();
}
}
Web存储 API 还包含一种侦听和响应任何本地存储变更的方法。 通过添加一个事件侦听程序以及侦听一个storage
事件,应用程序能够对localStorage
变更进行响应。 事件中的数据包含已更改的key的名称、新值、老值(如果有的话)以及调用该API的页面的URL。 可以利用下面的方式实现localStorage API的规范要求,即发起事件的会话将不能看到该事件的触发。 这是因为规范要求该事件只能针对其它、而不是更改存储的标签或会话进行触发。
为了侦听存储事件,首先需要做的事情是添加事件侦听程序:
window.addEventListener("storage",onStorageChange);
然后建立 onStorageChange
事件来处理存储事件。
function onStorageChange(e) {
if (e.key == "name") {
alert(e.newValue + ' just added their name to local storage');
}
}
另外,有一个创建数据的类似API,它只对个体会话持续有效。 通过使用sessionStorage
对象,而不是localStorage
对象,当用户离开页面时,任何保存的数据将被自动清除。 事实上,API 具有完全相同的方法,因此你可以仔细检查并且利用sessionStorage
替换localStorage
,然后本地数据将被保存直到用户关闭他们的浏览器或其中包含应用程序的标签。
离线存储
有时在用户的机器中仅仅存储一些数据是不能满足要求的。 在许多情形下,整个应用程序必须离线运行,而不仅仅存储一些数据。 对于这种使用情形,HTML5 包含了在用户机器上高速缓存文件和资源的功能,以便浏览器在没有因特网连接的情形下访问它们。 这意味着构成web应用程序的图像、JavaScript 文件、 HTML 文件、 CSS文件等大量数据能够本地存储,甚至在没有因特网连接的情形下能够对它们进行访问。 这一功能的关键是建立一个高速缓存的Manifest文件。
使用 manifest文件
Manifest文件是页面的根 HTML标签的新manifest
属性的一个组成部分。 它是一个位于web服务器上的文件,它能够列出浏览器应该下载和保存以便以后使用的所有文件。 它具有一个 .manifest 扩展名并且其唯一主要的gotcha 是相应的web 服务器必须支持 .manifest mime类型,因此,应该确保驻留应用程序的该 web 服务器能够正确提供.manifest 文件。
Manifest文件具有一个基本架构。 每个manifest 文件以 CACHE MANIFEST
开头,并且从这里开始,列出所有浏览器需要高速缓存的、用于离线访问的文件。 下面是一个简单范例,它能够存储一些JavaScript、一个 CSS 文件、一些图像和 相应的HTML页面:
CACHE MANIFEST style.css offlinescript.js images/dreamweaver_logo.png images/edge_logo.png
相应的路径均与用户正在访问的HTML页面相关。 当创建高速缓存manifest文件时,你必须了解一些其它选项。 其中一个选项是绝不能高速缓存的文件的情形。 也许只有能够在线获得的动态脚本或某些内容才是有意义的。 高速缓存manifest文件能够划分为告知浏览器如何对某些内容进行响应的区段(section)。 通过创建一个 NETWORK
和列出那些绝不能高速缓存的文件,浏览器一定能够忽略这些文件并且让人们决不能离线获得它们。
另一个情形是当用户试图访问一个没有高速缓存的页面或某些应该高速缓存但却没有正确保存的内容时的情形。 高速缓存 manifest API 能够提供一个FALLBACK
区段(section),它指向一个在上述用例中加载的页面。 因此,当用户试图访问没有保存的某些内容时,他们将看到一条关于离线提示的消息。 下面是一个理论上的包含这些区段(section)的高速缓存Manifest文件的大概架构:
CACHE MANIFEST NETWORK: my_dynamic_script.cgi FALLBACK: my_offline_message.html CACHE: style.css offlinescript.js images/dreamweaver_logo.png images/edge_logo.png
在本例中,我提供了一个带有一个外部JavaScript 页面和外部 CSS页面的HTML页面。 该HTML页面能够显示一些描述一个Adobe徽标的文本,并且当你点击相应的图像时,JavaScript 将会为另一个徽标换出相应的图像和文本。 下面是相应的HTML代码,紧跟其后的是JavaScript函数:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" manifest="cache.manifest">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Adobe Logos</title>
<script src="offlinescript.js"></script>
<link href="style.css" rel="stylesheet" />
</head>
<body>
<div id="textContent">
This is the Edge logo:</div>
<br />
<img id="logo" name="logo" src="images/edge_logo.png" onclick="onLogoClick();" />
<p class="small">
Click on the logo to swap it out.</p>
</body>
</html>
<!--
//JavaScript Document
function onLogoClick(e) {
var currentContent = window.document.getElementById("textContent").innerHTML;
if (currentContent == "This is the Edge logo:") {
window.document.getElementById("textContent").innerHTML = "This is the Dreamweaver logo:";
window.document.logo.src = "images/dreamweaver_logo.png";
}
else {
window.document.getElementById("textContent").innerHTML = "This is theEdge logo:"; window.document.logo.src = "images/edge_logo.png";
}
}-->
所有这些代码的关键部分是带有 manifest
属性的HTML标签。 它是指向我在上面引用的我的cache.manifest 文件。 该manifest文件能够指示浏览器下载列表中给出的所有文件。 不管用户在浏览这些文件时是否下载它们,相应的浏览器将自动下载manifest文件中包含的所有文件。 这意味着两个图像都将被保存以便离线访问,即使第二个图像直到我与相应内容互动时才加载到页面。 因此,只需加载该页面一次,我即可以在离线情形下完全与它进行互动,并且两个图像均会旋转。
了解事件
需要略微提到的、但却是非常重要的离线访问的最后部分是发生在高速缓存过程中的事件。 当浏览器遇到 manifest 属性,它将在window.applicationCache 对象中触发一系列事件。 第一个发生的事件是触发一个checking事件。 该事件可以确定需要利用这一特别的高速缓存文件进行哪些操作。 Google Chrome 开发人员工具可以在高速缓存区保存数据时能够全面地核查发生的事件(参见图1)。
![在高速缓存区保存数据时发生的事件 在高速缓存区保存数据时发生的事件](http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/html5/articles/html5-storage-apis/fig01.jpg)
如果这是用户第一次访问该网站,则将触发一个下载事件并且相应的web浏览器将仔细查看并且下载manifest文件中包含的所有资源。 它能够读取相应的manifest文件,确定它需要下载多少文件,然后以progress事件的形式为每个文件回送状态更新信息。 Progress 事件包含一个已加载的变量和一个总变量,这样开发人员能够确定高速缓存区已经存储的多少内容。
function onProgress(e) {
var content = window.document.getElementById("loadedInfo").innerHTML;
window.document.getElementById("loadedInfo").innerHTML = content + '<br /> Loaded file ' + e.loaded + ' of ' + e.total;
}
当完成所有文件的保存操作时,浏览器将触发一个cached事件通知开发人员所有用于离线使用的文件已经成功保存。
在后面用户访问页面的任何时刻,浏览器将核查看一看在manifest 文件中是否有内容发生改变。 如果没有,它将触发一个 noupdate
事件,然后继续运行。 如果其中有内容发生改变,则它将经历与上面完全相同的过程;它将触发一个包含一系列progress
事件的downloading
事件,直到相应的文件全部更新完毕。 当该事件发生时,而不是触发一个cached
事件,浏览器将触发一个updateready
事件表示所有的文件已经更新并且可以离线使用。
最后一个令人发愁的事件是error
事件,它将在应用程序出现故障时触发。 这些故障可能是文件不能正确加载,浏览器不能访问cache manifest文件,或manifest列出的一个或多个文件不存在等。 为了捕捉这些故障,只需为error
事件添加一个事件侦听程序。
window.applicationCache.addEventListener("error", onError); function onError(e) {
window.document.getElementById("loadedInfo").innerHTML = "Something went wrong while saving the files for offline use.";
}
数据库存储
HTML5引入的最后一个存储类型是目前最处于不断变化之中的类型。 最初有一个 Web SQL规范,但它现在已经不再使用。 现在,大多数人的精力已经转移到Indexed Database API 上,并且似乎这将是在关系型数据库中存储信息的出路。 Firefox和 Chrome 均支持IndexedDB,但由于相应的规范和支持功能均处于不断变化之中,所以它超出本文的讨论范围。 在将来某个时候,当这一状态改变时,我将进行相应的更新。
下一步阅读方向
在本文中,我探索了两种本地存储信息的主要方法。 第一种方法以web存储API的形式提供一些非常基本的数据存储技术。 利用这一方法,你可以在会话之外或为一个单一会话保存名称值对(name value pair)。 第二种方法,即应用程序cache manifest,允许开发人员在本地机器上保存所有文件,以便可以在离线情形下访问它们。
关于HTML5存储API的更多信息,参见下列资源: