- 简介
- 历史
- 历史(续)
- 历史(续二)
- 历史(续三)
- 检测 HTML5 特性
- 检测 HTML5 特性(续)
- 检测 HTML5 特性(续二)
- 检测 HTML5 特性(续三)
- 它的含义是什么?
- 它的含义是什么?(续)
- 它的含义是什么?(续二)
- 它的含义是什么?(续三)
- 它的含义是什么?(续四)
- 它的含义是什么?(续五)
- 绘图
- 绘图(续)
- 绘图(续二)
- 绘图(续三)
- 绘图(续四)
- 绘图(续五)
- Web 视频
- Web 视频(续)
- Web 视频(续二)
- Web 视频(续三)
- Web 视频(续四)
- 地理位置
- 地理位置(续)
- 本地存储
- 本地存储(续)
- 离线 Web 程序
- 离线 Web 程序(续)
- 表单
- 表单(续)
- 表单(续二)
- 可扩展性
- 可扩展性(续)
- 可扩展性(续二)
- 可扩展性(续三)
- 可扩展性(续四)
- 历史 API
本地存储(续)
浏览器限制
前面我们讨论了使用第三方插件实现本地存储的历史,我们指出了每种实现技术的不足之处,例如存储限制等。然而,新的 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
记录游戏是不是正在进行( gGameInProgress
, Boolean
)。如果是,则遍历棋子数组( gPieces
,JavaScript Array),保存每个棋子的行和列的值。然后保存下游戏其他状态,包括哪个棋子被选中( gSelectedPieceIndex
,整型),是否有棋子在连跳过程中( gSelectedPieceHasMoved
, Boolean
),以及总步数( 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 现在有四个浏览器或平台支持:
IE | Firefox | Safari | Chrome | Opera | iPhone | Android |
– | – | 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论