返回介绍

本地存储(续)

发布于 2025-01-01 12:18:10 字数 6413 浏览 0 评论 0 收藏 0

浏览器限制

前面我们讨论了使用第三方插件实现本地存储的历史,我们指出了每种实现技术的不足之处,例如存储限制等。然而,新的 HTML5 Storage 标准依然有一些不足。现在,我们就要批判地看待这个问题,然后再详细地解释一下。HTML5 Storage 的不足有三个:“5 MB”,“QUOTA_EXCEEDED_ERR” 和 “不行!”。

“5 MB”是指默认情况下每一源头的存储空间大小。这个问题在不同浏览器的实现竟然惊人的一致,虽然对于 HTML5 Storage 标准仅仅将其作为一种建议。你需要记住一件事:你存储的是字符串,而不是数据的原始格式。如果你在存储很多的整数或者浮点数,这种差异就显现出来了。浮点数中的每一个数字都是作为一个字符存储的,而不是计算机内部浮点数的真实表示法。

“QUOTA_EXCEEDED_ERR” 是一个异常。如果你的存储超过了 5 MB,就会发出这个异常。“不行”则是下一个显然的问题的答案:“我可以要求更大的存储空间吗?”在本文写作的时间(2011 年 2 月),还没有浏览器支持 web 开发人员请求更大的存储空间。有些浏览器(例如 Opera)允许用户控制每一个网站的存储空间大小,但是那仅仅是一个用户限制,不是作为一个 web 开发人员能够控制的。

HTML5 Storage 实战

下面我们开始尝试实际使用一下 HTML5 Storage。还记得我们曾经开发过一个 跳棋游戏 吗?我们的游戏还有一个小问题:如果在游戏过程中关闭浏览器,你就丢失掉全部进度了。但是,如果我们使用 HTML5 Storage,我们就可以将游戏进度保存到本地,就不会有这个问题。这里是我们已经 做好的版本 。试着走几步,然后关闭浏览器,再打开看看。如果你的浏览器支持 HTML5 Storage,我们的游戏就可以保存下你关闭浏览器时的棋子位置,甚至你当时选中的棋子。

这是如何实现的呢?我们每走一步都会调用这个函数:

function saveGameState() {
    if (!supportsLocalStorage()) { return false; }
    localStorage["halma.game.in.progress"] = gGameInProgress;
    for (var i = 0; i < kNumPieces; i++) {
	localStorage["halma.piece." + i + ".row"] = gPieces[i].row;
	localStorage["halma.piece." + i + ".column"] = gPieces[i].column;
    }
    localStorage["halma.selectedpiece"] = gSelectedPieceIndex;
    localStorage["halma.selectedpiecehasmoved"] = gSelectedPieceHasMoved;
    localStorage["halma.movecount"] = gMoveCount;
    return true;
}

正如你看到的那样,我们使用 localStorage 记录游戏是不是正在进行( gGameInProgressBoolean )。如果是,则遍历棋子数组( gPieces ,JavaScript Array),保存每个棋子的行和列的值。然后保存下游戏其他状态,包括哪个棋子被选中( gSelectedPieceIndex ,整型),是否有棋子在连跳过程中( gSelectedPieceHasMovedBoolean ),以及总步数( gMoveCount ,整型)。

页面加载时,我们不能调用 newGame() 函数,因为这个函数会将所有变量清零。我们需要调用的是 resumeGame() 函数。通过 HTML5 Storage, resumeGame() 函数检查是否有游戏过程存储在本地的状态。如果有,则从 localStorage 对象将这些值恢复出来。

function resumeGame() {
    if (!supportsLocalStorage()) { return false; }
    gGameInProgress = (localStorage["halma.game.in.progress"] == "true");
    if (!gGameInProgress) { return false; }
    gPieces = new Array(kNumPieces);
    for (var i = 0; i < kNumPieces; i++) {
	var row = parseInt(localStorage["halma.piece." + i + ".row"]);
	var column = parseInt(localStorage["halma.piece." + i + ".column"]);
	gPieces[i] = new Cell(row, column);
    }
    gNumPieces = kNumPieces;
    gSelectedPieceIndex = parseInt(localStorage["halma.selectedpiece"]);
    gSelectedPieceHasMoved = localStorage["halma.selectedpiecehasmoved"] == "true";
    gMoveCount = parseInt(localStorage["halma.movecount"]);
    drawBoard();
    return true;
}

这个函数最重要的部分是我们前面反复警告过的一件事:所有数据都是以字符串的形式存储的。如果你存储的不是字符串,就需要自己进行类型转换。例如,游戏是否正在进行的标记( gGameInProgress )是一个 Boolean 类型。在 saveGameState() 函数中,我们仅仅将其存了下来,没有管它是什么类型:

localStorage["halma.game.in.progress"] = gGameInProgress;

但是在 resumeGame() 函数,我们需要从本地存储中以字符串的形式读取这些值,然后再手动做类型转换:

gGameInProgress = (localStorage["halma.game.in.progress"] == "true");

类似的,我们也需要将移动步数 gMoveCount 转换成整数。在 saveGameState() 函数中,我们这样存储这个值:

localStorage["halma.movecount"] = gMoveCount;

但是在 resumeGame() 函数中,我们需要将其值转换成整数。我们使用的是 JavaScript 内置的 parseInt() 函数:

gMoveCount = parseInt(localStorage["halma.movecount"]);

存储键值对类型以外的数据

相对于过去的种种技巧,HTML5 Storage 的前景确实相当乐观。一个新的 API 已经被标准化,并且在所有主流浏览器、平台和设备上已经实现。作为一个 web 开发人员,这并不是每天都能看到的事情。但是,我们必须承认,还有很多东西是“5MB 键值对”所不能存储的。持久化存储应该支持更多格式。

其中一种观点是你已经熟悉了的:SQL。2007 年,Google 发布了 Gears,作为一个跨浏览器的开源插件,它包含了一个基于 SQLite 的嵌入式数据库。这是 Web SQL Database 标准的早期原型。Web SQL Database(通常称为 WebDB)提供了一个 SQL 数据库的简单封装,允许你使用 JavaScript 做这样的事情:

openDatabase('documents', '1.0', 'Local document storage', 5*1024*1024, function (db) {
    db.changeVersion('', '1.0', function (t) {
        t.executeSql('CREATE TABLE docids (id, name)');
    }, error);
});

正如你看到的那样,这段代码中核心字符串在 executeSql 函数。这个字符串可以是任何支持的 SQL 语句,包括 SELECT、UPDATE、INSERT 和 DELETE。就像后台数据库编程一样,唯一区别是你用的是 JavaScript。

Web SQL Database 现在有四个浏览器或平台支持:

IEFirefoxSafariChromeOperaiPhoneAndroid
4.0+4.0+10.5+3.0+2.0+

如果你曾经使用过数据库产品,就应该知道“SQL”有许多商业实现,而不仅仅是一个标准。(有人会说 HTML5 也是这个样子,但这有些区别。)当然,我们也有一些事实上的 SQL 标准,称为 SQL-92,但是现在没有数据库仅仅提供标准支持的操作。我们有 Oracle 的 SQL,Microsoft 的 SQL,MySQL 的 SQL,PostgreSQL 的 SQL 和 SQLite 的 SQL。事实上,每一个数据库产品都会增加自己独特的 SQL 特性,所以,当我们说 “SQLite 的 SQL” 的时候,你应该意识到这意味着什么。同时你需要指明,这是 “ SQLite X.Y.Z 版本所支持的 SQL”。

鉴于 SQL 这种不确定性,Web SQL Database 标准做了如下描述:

本标准实际已经陷入僵局:所有感兴趣的实现都使用了同一个 SQL 后端(SQLite),但是,我们需要多种独立实现,以便形成一种独立的标准。在另外一种实现出现之前,SQLite 的一个简单实现已经被选为 SQL 方言,不过,对于一个通用标准而言,这是不可接受的。

正是在这种背景下,我们引入了另外一种高级的、持久化的、本地存储机制:Indexed Database API,通常被称为 WebSimpleDB,现在则叫做 IndexedDB。

Indexed Database API 使用的是一种对象存储机制。对象存储与 SQL 数据库有许多共同点:都有“数据库”和“记录”的概念,每一个记录都有一组“属性”。每一个属性都要有一个指定的数据类型,这个数据类型在创建数据库的时候就被定义好了。你可以选择所有记录的一个子集,然后使用“游标”进行遍历。对对象的修改则通过所谓的“事务”进行。

如果你曾经做过 SQL 数据库编程,就会发现这些概念都十分雷同。二者主要区别在于,对象存储没有结构化查询语言的概念。你不需要类似” SELECT * from USERS where ACTIVE = 'Y' “这种语句。你需要做的是使用对象存储机制提供的函数,在名为 USERS 的数据库上打开一个游标,遍历所有记录,过滤掉不活动的用户,使用访问函数读取剩下记录的所有字段。 An early walk-through of IndexedDB 是一篇关于 IndexedDB 如何工作的不错的教程,给出了关于 IndexedDB 和 Web SQL Database 的对比。

在本文写作的时候,IndexedDB 仅有 Firefox 4 实现了。(同时,Mozilla 宣布他们不准备实现 Web SQL Database。)Google 也宣布他们将考虑为 Chromium 和 Google Chrome 添加 IndexedDB 的支持。Microsoft 也认为 IndexedDB “是一个不错的解决方案”。

那么,作为一个 web 开发者,你能使用 IndexedDB 吗?目前,我们还没有任何技术上的演示程序。那么,一年以后呢?或许吧。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文