在本文中,我将介绍如何创建一个基本的笔记应用程序,它可以在您在线时同步本地和远程内容,并在离线时默认保存到本地。
在服务器端使用 Redis
在 Redis 中添加记录时,我们不是像在 MySQL 或 PostgreSQL 中那样处理关系型数据库。我们处理的是类似于 IndexedDB 的结构,其中有键和值。那么,当我们只有一个键和值用于笔记应用程序时,我们需要什么?我们需要唯一 ID 来引用每个笔记,以及笔记元数据的哈希值。在本例中,元数据包含新的唯一 ID、创建时间戳和文本。
以下是在 Node 中使用 Redis 创建 ID 并保存笔记元数据的示例。
// Let's create a unique id for the new note.
client.incr('notes:counter', function (err, id) {
...
// All note ids are referenced by the user's email and id.
var keyName = 'notes:' + req.session.email + ':' + id;
var timestamp = req.body.timestamp || Math.round(Date.now() / 1000);
// Add the new id to the user's list of note ids.
client.lpush('notes:' + req.session.email, keyName);
// Add the new note to a hash.
client.hmset(keyName, {
id: id,
timestamp: timestamp,
text: finalText
});
...
});
这将为服务器端的所有笔记提供以下键模式
notes:counter
包含从 1 开始的所有唯一 ID。notes:<email>
包含用户拥有的所有笔记 ID。这是一个列表,我们在需要循环遍历所有用户的笔记以检索元数据时会引用它。notes:<email>:<note id>
包含笔记元数据。用户的电子邮件地址用于将此笔记引用到正确的拥有者。当用户删除笔记时,我们想要验证它是否与他们登录的相同电子邮件匹配,因此不会有人删除他们不拥有的笔记。
在客户端添加 IndexedDB
使用 IndexedDB 需要比 localStorage 更多的代码。但由于它是异步的,因此它更适合此应用程序。它更适合的两个主要原因是
- 您不希望等待所有笔记处理完毕后才渲染页面上的所有元素。想象一下,如果有数千个笔记,您必须等待所有笔记循环完毕才能在页面上显示任何内容。
- 您不能将笔记对象保存为对象,必须先将它们转换为字符串,这意味着在渲染之前必须将它们转换回对象。例如
{ id: 1, text: 'my note text', timestamp: 1367847727 }
必须在 localStorage 中被字符串化,然后在之后被解析回来。现在想象一下,对很多笔记执行这个操作。
这两者都不等同于用户的理想体验,但如果我们想要 localStorage API 的易用性和 IndexedDB 的异步特性呢?我们可以使用 Gaia 的 async_storage.js 文件来帮助合并这两个世界。
如果我们离线,我们需要执行与服务器端类似的两件事
- 为笔记保存一个唯一 ID 并将其应用于 ID 数组中。由于我们无法引用由 Redis 创建的服务器端 ID,因此我们将使用时间戳。
- 保存笔记元数据的本地版本。
var data = {
content: rendered,
timestamp: id,
text: content
};
asyncStorage.setItem(LOCAL_IDS, this.localIds, function () {
asyncStorage.setItem(LOCAL_NOTE + id, data, function () {
...
});
});
IndexedDB 键的结构与 Redis 的结构非常相似。模式如下
- 所有本地 ID 都保存在
localNoteIds
数组中 - 所有本地笔记对象都保存在
note:local:<id>
中 - 所有远程/同步 ID 都保存在
noteIds
数组中 - 所有远程/同步笔记对象都保存在
note:<id>
中 - 本地笔记使用时间戳作为其唯一 ID,并在 Redis 保存数据后将其转换为有效的服务器 ID
一旦我们在线,就可以上传本地笔记,在客户端保存远程笔记,然后删除本地笔记。
在客户端触发 note.js
每当我们刷新页面时,都需要尝试与服务器同步。如果我们离线,让我们标记它,只获取我们本地的内容。
/**
* Get all local and remote notes.
* If online, sync local and server notes; otherwise load whatever
* IndexedDB has.
*/
asyncStorage.getItem('noteIds', function (rNoteIds) {
note.remoteIds = rNoteIds || [];
asyncStorage.getItem('localNoteIds', function (noteIds) {
note.localIds = noteIds || [];
$.get('/notes', function (data) {
note.syncLocal();
note.syncServer(data);
}).fail(function (data) {
note.offline = true;
note.load('localNoteIds', 'note:local:');
note.load('noteIds', 'note:');
});
});
});
快要完成了!
上面的代码提供了具有本地和远程同步支持的 CRD 笔记应用程序的基础。但我们还没完。
在 Safari 上,IndexedDB 不受支持,因为它们仍然使用 WebSQL。这意味着我们的 IndexedDB 代码将无法工作。为了使其跨浏览器兼容,我们需要包括一个 仅支持 WebSQL 的浏览器的 polyfill。在其他代码和 IndexedDB 支持之前包含它,应该可以正常工作。
最终产品
您可以在 http://notes.generalgoods.net 尝试使用该应用程序
源代码
要查看此应用程序的代码,请随时浏览 Github 上的仓库.
关于 Robert Nyman [名誉编辑]
Mozilla Hacks 的技术布道者和编辑。进行演讲并撰写有关 HTML5、JavaScript 和开放网络的博客。Robert 是 HTML5 和开放网络的坚定支持者,自 1999 年以来一直在从事网络前端开发工作——在瑞典和纽约市。他还经常在 http://robertnyman.com 上撰写博客,喜欢旅行和结识新朋友。
4 条评论