众所周知,最好的框架和工具并非凭空创造,而是提取而来。自推出 Firefox OS 以来,Mozilla 一直受到无数应用开发人员和 Web 开发人员提出的一个简单问题困扰:“如何为 Firefox OS 开发应用?” 答案:“它是 Web;使用现有的 Web 技术。” 过去是,现在仍然是一个很好的答案。
但是,如果您作为 Web 开发人员还没有现有的工具链,我一直在努力从我在 Mozilla 创建 Web 应用的方式中提取一些东西,您可以使用这些东西来编写您的下一个 Web 应用。从项目创建到模板化再到部署,Mozilla 的 Rec Room 将帮助您更快、更轻松地创建出色的 Web 应用。
Rec Room 是一个您可以用来构建客户端 Web 应用的 Node.js 工具集。它包括
- Brick 用于向您的 UI 添加应用栏和按钮等组件。
- Ember 用于应用的控制器、模型和视图。
- Handlebars 用于编写应用的模板。
- Grunt 用于运行应用的任务,包括构建生产版本。
- I18n.js 用于本地化您的应用。
- Mocha 用于测试您的应用。
- Stylus 用于编写 CSS。
- Yeoman 用于为应用的模型和模板搭建新的代码。
在这篇文章中,我将逐步介绍如何使用 Rec Room 创建一个简单的世界时钟 Web 应用,如何部署它,以及如何自己尝试使用 Rec Room。
Rec Room 来自哪里?
Rec Room 的大部分内容来自最近对HTML5 播客应用的重写。我开始着手开发这个应用已经超过一年了,但它的原始版本并不容易使用;它有很多全局状态和很多手动的 数据绑定。我喜欢使用 Ember 进行应用开发,但是当我开始使用它时,它还不太成熟。如今它变得好多了,我在 Rec Room 中对其进行了调整,使其能够在没有服务器的情况下完美运行。
我试图从该系统中提取出最好的部分,并将其提取成一套任何人都可以使用的工具和文档。
创建您自己的 Rec Room 应用
Rec Room 最近才从我对播客的经验中提取出来;它还没有经过超过少数开发人员的测试。也就是说:我们非常乐意您帮助尝试使用这些工具构建您自己的 Firefox OS 应用。它们与您可能已经了解并使用的工具(如 Node.js 和 Firefox 自身的 Web IDE)很好地集成在一起。
要开始使用,请使用 Node.js 安装 Rec Room
npm install -g recroom
时钟应用
我们将创建一个简单的时钟应用,其中包含(最少的)时区支持作为我们的示例。该应用将允许您查看时钟并将其与几个时区进行比较。
recroom
二进制文件是 Rec Room 可以为您做所有酷事的入口点。首先,使用 recroom new world-clock
创建您的应用。这将创建基本的应用结构。要查看 Rec Room 创建的基本应用框架,我们现在可以进入该目录并运行我们的应用:cd world-clock
,然后键入 recroom run
。该应用将在您的默认浏览器中打开。
首先,我们将当前时间添加到主选项卡中。Rec Room 支持 Ember 的 MVC 应用结构,但也为不与模型具有 1:1 关系的控制器提供了简单的“页面”。我们将生成一个新页面,该页面将显示我们的实际时钟
recroom generate page Clock
我们可以通过打开 app/templates/clock.hbs
来编辑其模板。让我们更改 clock.hbs
以包含将输出我们本地时间的变量
Local Time: {{localTime}}
这还没有什么作用,所以让我们将该变量添加到我们的 ClockController
中,在 app/scripts/controllers/clock_controller.js
中
WorldClock.ClockController = Ember.ObjectController.extend({
localTime: new Date().toLocaleTimeString()
});
您可以看到,控制器内的任何属性都可以在该控制器的模板内访问。我们定义了 1ocalTime
属性,它会被传递到我们的模板上下文中。
现在,当我们导航到 http://localhost:9000/#clock
时,我们的时钟应用将显示当前本地时间。当然,它只显示控制器初始化时的时刻;时间没有实时更新。我们应该在控制器内每秒更新一次时间
WorldClock.ClockController = Ember.ObjectController.extend({
init: function() {
// Update the time.
this.updateTime();
// Run other controller setup.
this._super();
},
updateTime: function() {
var _this = this;
// Update the time every second.
Ember.run.later(function() {
_this.set('localTime', new Date().toLocaleTimeString());
_this.updateTime();
}, 1000);
},
localTime: new Date().toLocaleTimeString()
});
现在,我们可以转到我们的时钟 URL,并看到我们的时钟每秒自动更新一次。这要归功于 Ember 在控制器和模板之间的数据绑定;如果我们更改控制器、模型或与模板连接的视图中的值,模板将自动为我们更改该数据。
添加时区
接下来,我们希望添加一些时区,用户可以将其添加到自己的时区集合中以与本地时间进行比较。这将帮助他们安排与旧金山、布宜诺斯艾利斯和伦敦的朋友的会议。
我们可以使用相同的 generate
命令创建时区模型(以及相应的控制器/路由/模板),但这次我们将生成一个模型
recroom generate model Timezone
我们希望我们要在我们的应用中包含的每个时区都具有名称和偏移量值,因此我们应该将它们添加为模型属性。为此,我们使用 Ember Data,在 app/scripts/models/timezone_model.js
中
WorldClock.Timezone = DS.Model.extend({
name: DS.attr('string'),
offset: DS.attr('number')
});
接下来,我们将需要一个所有时区的列表以供用户选择。为此,我们将获取 Moment Timezone 的副本。它是一个用于处理 JavaScript 中日期和时间的强大 JavaScript 库。我们将使用 bower 来安装它
bower install moment-timezone --save
然后将其添加到 app/index.html
中的应用中
添加该标签将自动将 moment-timezone-with-data-2010-2020.js
添加到我们的构建应用中。我们将在页面上添加一个选项卡,让我们在与时钟不同的屏幕上编辑我们的时区。要添加选项卡,我们只需要打开 app/templates/application.hbs
并添加一个选项卡即可。当我们在那里时,我们将主选项卡从无用的 {{#linkTo 'index'}}
更改为 {{#linkTo 'clock'}}
。新的 application.hbs
应该如下所示
{{t app.title}}
{{outlet}}
旁注:注意根 URL 指向一个无用的欢迎页面?我们可能希望默认路由为我们的 ClockController
,这样我们可以将索引路由设置为重定向到它。让我们现在在 app/scripts/routes/application_route.js
中执行此操作
WorldClock.ApplicationRoute = Ember.Route.extend({
redirect: function() {
this.transitionTo('clock');
}
});
与时区模型交互
我们将为我们的示例保持简单,并允许用户从 <select>
标签中选择一个时区,并使用按钮添加它。它将显示在他们的时区列表中,如果他们愿意,他们可以从那里删除它。时钟选项卡将显示所有时间。首先,我们将从 Moment.js 中的 TimezonesController
中的 app/scripts/controllers/timezones_controller.js
中添加我们的时区数据。我们还将实现两个操作:“添加”和“删除”。这些将在我们的模板中使用
WorldClock.TimezonesController = Ember.ObjectController.extend({
init: function() {
var timezones = [];
for (var i in moment.tz._zones) {
timezones.push({
name: moment.tz._zones[i].name,
offset: moment.tz._zones[i].offset[0]
});
}
this.set('timezones', timezones);
this._super();
},
selectedTimezone: null,
actions: {
add: function() {
var timezone = this.store.createRecord('timezone', {
name: this.get('selectedTimezone').name,
offset: this.get('selectedTimezone').offset
});
timezone.save();
},
remove: function(timezone) {
timezone.destroyRecord();
}
}
});
因此,我们创建了一个包含所有可用时区及其偏移量的列表。然后,我们添加了允许我们向离线数据存储中添加或删除时区的方法。接下来,我们修改 app/templates/timezones.hbs
中的时区模板以使用我们创建的操作和变量。我们只需要利用这些变量即可使用 Ember SelectView
和 {{action}}
辅助函数来调用我们的 add
和 remove
方法
Add Timezone
{{view Ember.Select content=timezones selection=selectedTimezone
optionValuePath='content.offset' optionLabelPath='content.name'}}
My Timezones
-
{{#each model}}
- {{name}} {{/each}}
现在,我们有一个时区选项卡,允许我们添加和删除我们想要跟踪的时区。这些数据在应用刷新之间保持不变。我们需要做的最后一件事是在我们的时钟选项卡中相对于我们的本地时间显示这些时间。为此,我们需要在 ClockRoute
中加载所有 Timezone
模型。它们在 TimezonesRoute
中自动加载,但很容易在 ClockRoute
(在 app/scripts/routes/clock_route.js
中)中添加它们
WorldClock.ClockRoute = Ember.Route.extend({
model: function() {
return this.get('store').find('timezone');
}
});
由于我们的 Ember 应用的连接方式,我们在路由中加载所有模型,并在数据存储异步加载所有模型后将它们发送到控制器。对 find('timezone')
的请求实际上返回一个 Promise
对象,但 Ember 的路由器会自动处理 Promise 的解析,因此我们不必自己管理回调或 Promise。
现在,我们可以访问 ClockController
中用户的所有时区,因此我们可以计算用户请求的每个时区的时间,并在列表中显示它们。首先,我们将每个时区的当前时间添加到 app/scripts/controllers/clock_controller.js
中的 ClockController
中,使用 Moment.js
WorldClock.ClockController = Ember.ObjectController.extend({
updateTime: function() {
var _this = this;
// Update the time every second.
Ember.run.later(function() {
_this.set('localTime', moment().format('h:mm:ss a'));
_this.get('model').forEach(function(model) {
model.set('time',
moment().tz(model.get('name')).format('h:mm:ss a'));
});
_this.updateTime();
}, 1000);
}.on('init'),
localTime: moment().format('h:mm:ss a')
});
我们最终的 app/templates/clock.hbs
应该如下所示
Local Time: {{localTime}}
{{#each model}}
{{name}}: {{time}}
{{/each}}
就是这样!现在,我们有一个离线应用,可以向我们显示各个地方的时区,离线保存数据,并在我们不必做太多工作的情况下每秒更新一次!
命令行工具
旧的播客应用使用了一个(相当糟糕的) Makefile。它不是很有用,而且我认为它在没有付出一些巨大努力的情况下无法在 Windows 上运行。新的构建系统使用 Node,因此它可以在 Windows、Mac 和 Linux 上轻松运行。命令通过用 Node 编写的 recroom
二进制文件代理,因此如果您不需要修改构建步骤,则不必担心底层系统。recroom new my-app
创建一个新应用;recroom serve
提供您的新应用,而 recroom generate model Podcast
为您创建了一个新模型。
要构建您的应用,您只需要运行 recroom build
,一个包含最小化 CSS、JS 甚至 HTML 的版本将为您创建,位于 dist/
文件夹中。此版本已准备好打包到打包应用中或作为托管应用上传到服务器。您甚至可以运行 recroom deploy
将目录部署到您的 Git 存储库的 GitHub Pages 分支,如果适用。
查看应用的实际效果!
整个示例应用可在 worldclock.tofumatt.com 上获得,并且源代码可在 GitHub 上获得。
尝试在您的下一个 Web 应用中使用 Rec Room
您可以在 Github 上试用 Rec Room。目前,一些文档和工具仍在抽象和构建中,但您可以立即开始使用它构建应用并为缺少的功能提交错误。我们非常希望您能尝试一下并告诉我们缺少什么。我们可以一起构建一个连贯且完善的解决方案,以解决一个过于常见的问题:“如何构建 Web 应用?”
关于 Matthew Riley MacPherson
Matthew Riley MacPherson(又名 tofumatt)是一位生活在 Pythonista 世界中的 Rubyist。他来自加拿大,因此您会发现他的写作中有很多奇怪的拼写(例如“colour”或“labour”)。他对漂亮的代码、优质的咖啡和非常快的摩托车有着浓厚的兴趣。查看他在 GitHub 上的代码 或 在 Twitter 上与他讨论摩托车。
Matthew Riley MacPherson 的更多文章…
关于 Robert Nyman [荣誉编辑]
Mozilla Hacks 的技术布道师和编辑。发表关于 HTML5、JavaScript 和开放 Web 的演讲和博客文章。Robert 坚信 HTML5 和开放 Web,并且自 1999 年以来一直在从事 Web 前端开发工作——在瑞典和纽约市。他还定期在 http://robertnyman.com 上发布博客文章,并且喜欢旅行和结识新朋友。
23 条评论