是时候开始黑客了 – 介绍 Rec Room

众所周知,最好的框架和工具并非凭空创造,而是提取而来。自推出 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}}
{{#link-to 'clock'}}Clock{{/link-to}} {{#link-to 'timezones'}}Timezones{{/link-to}}

旁注:注意根 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}} 辅助函数来调用我们的 addremove 方法

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 上发布博客文章,并且喜欢旅行和结识新朋友。

Robert Nyman [荣誉编辑] 的更多文章…


23 条评论

  1. Mario Valente

    现在,我们所需要的只是 Mozilla 能够觉醒并从 Spidermonkey 中为我们提供一个服务器端 JS 环境,作为异步 NodeJS 回调地狱的替代方案。

    2014 年 8 月 18 日 11:05

    1. Fawad Hassan

      我也希望我们有一个像 Node.js 这样的服务器端技术,但我认为启动这样的项目是浪费精力。

      JavaScript 确实为回调地狱提供了解决方案,例如 Promises、Generators 和 async/await(目前为 W3C 草案)。看看 Koa 框架 (koajs.com),它广泛使用生成器来避免异步编码风格的复杂性。

      2014 年 8 月 18 日 11:30

    2. Nick Desaulniers

      更改运行时引擎不会更改语言。请参阅 ES6 Promises。

      2014 年 8 月 18 日 16:11

  2. 不胜感激

    哇,我迫不及待地想使用所有流行的 Web 技巧来构建一个移动应用。哦,等等——是的,我可以。Web 技术很糟糕,Mozilla 应该为继续使用它而感到难过。

    为什么不构建一个面向应用开发的平台,而不是成为过去错误决策的奴隶呢?

    2014 年 8 月 18 日 15:22

    1. Robert Nyman [编辑]

      我们相信,Web 凭借所有各方提供的标准化功能和可能性,是拥有每个人都可以使用的媒介的关键。我们希望 Web 能够在所有平台上可用并成为一等公民。

      “Web 技术很糟糕,Mozilla 应该感到难过” 这是一个相当笼统的声明,但任何可以帮助改进 Web 技术的建设性反馈,请告诉我们。

      2014 年 8 月 19 日 01:14

  3. sudarshan

    嗨,我安装了 Mozilla 的 Rec Room。但在当前目录中运行命令 (recroom new world-clock) 时,它总是显示一个 recroom 文件,而不是创建基本模板。


    您能否提供您之前成功使用的路径,以便我再次尝试一下?
    很棒,Web OS 正在迈向新的发展阶段。

    2014年8月19日 05:43

    1. tofumatt

      我很乐意帮助您解决这个问题;您能否将其作为问题报告一下(https://github.com/mozilla/recroom/issues/new)?如果您能展示运行这些命令时创建的文件,那就太好了。谢谢!

      2014年8月19日 05:45

      1. WM

        我遇到了同样的问题,它只创建了一个空文件夹。
        并且命令行输出一直停留在
        “Creating your Rec Room project. This may take some time…”

        2014年8月19日 23:12

        1. tofumatt

          这看起来像是首次使用 Yeoman 用户遇到的一个 bug,我们在问题页面上跟踪它,我计划今天或明天发布修复程序 :-)

          2014年8月20日 06:40

  4. Michael Niemann

    我不是程序员。我先把这个说清楚。我做了一个应用,现在在应用商店里,我用文本编辑器和 Firefox 中的应用管理器创建了它,一行一行地编写代码,并参考了大量网站(包括 MDN)上的示例。我之前使用过 Brick 的 flipbox 模块,在它被修改为需要 node.js 之前。

    对于在这里提出这个问题我表示歉意,因为这里可能不是正确的地方。但我不知道还能在哪里问。node.js 以及所有似乎需要它的东西,实际上为我做了什么,比简单地编写 js 文件、css 文件和 html 文件更好在哪里?

    也许我应该问问我可以在哪里问这个问题。

    2014年8月19日 15:04

    1. tofumatt

      在这里问没问题! :-)

      听起来,对于您来说,使用 HTML、css 和 JS 的简单解决方案是最佳选择,这很好。我认为对于很多人来说都是如此,当然这取决于项目。我认为新 WebIDE 中提供的模板符合您的描述。但对于许多人来说,我听说他们想要更全面的功能。我知道这是我提高效率所需的。

      对我来说,这些需求让我能够构建具有复杂路由、模型交互等的离线应用,并且更容易构建。如果您的需求很简单,那么您就准备好了,但如果您的需求很复杂,我希望这些工具能够简化操作。

      2014年8月19日 15:24

      1. Michael Niemann

        感谢您的回复。它为我澄清了一些事情。我对学习新的做事方式很感兴趣,但当我看到像这样的帖子时,我会有一种错觉,感觉我错过了重要的东西。我想我会知道为什么我的需求不再简单了。

        2014年8月20日 08:19

    2. Andrew Fallows

      Node、Ember、Stylus 等工具以及 Rec Room 等工具集中包含的其余工具,其主要前提都归结为一件事:使开发过程更快。

      在技术上,每个基于 EmberJS 等构建的先进的现代 Web 应用都可以用纯 JavaScript 编写,而无需任何额外的工具。毕竟,一旦它到达浏览器,它就只剩下 HTML、CSS 和 JS——所有内容都会被编译和解释。这些工具的优势在于,开发人员花费更少的时间就能达到相同的完整性/功能水平。

      当然,权衡是需要更多时间来学习额外的工具,并且会引入更多可能导致开发过程失败的地方(除了逻辑错误之外,还有编译错误等)。

      对于那些有一个特定目标的人,尤其是一个小项目,很多这些东西可能是臃肿的开销;学习工具可能比不使用它解决问题花费更多的时间。对于那些(像我一样)定期开发大型应用并具有非常常见的重复模式来管理内容的人来说,简化这些模式实现的工具可以每周为我们节省数小时的时间。

      2014年8月21日 10:17

      1. Robert Nyman [编辑]

        感谢 Andrew 对需求和选项的非常好的描述!

        2014年8月22日 00:26

  5. Lucas

    您好,谢谢。
    我尝试按照本教程进行操作,当我第一次尝试访问 localhost:9000/#clock 时,Ember 在控制台中抛出“The route clock was not found”。
    我查看了您在 Github 上的代码,它在“router.js”和其他一些文件中有一些我的代码没有的东西。
    我不知道这是否是“recroom generate page”应该做的事情,或者您是否在教程中跳过了这一步,但无论哪种方式,可能都需要修复。

    我熟悉 JS,并且乐意为该项目做出贡献,因为它旨在解决 Web 开发新手(像我一样)遇到的问题。“标准开发套件”由 Mozilla 支持,这是我梦寐以求的。

    我不太了解 Ember 和 MVC,但我会尽我所能关注该项目的开发。感谢您投入到其中的时间!

    2014年8月20日 13:39

    1. Lucas

      我实际上自己解决了这个问题。Rec Room 脚本将主应用实例称为“App”,而不是“WorldClock”。此外,您需要添加

      this.resource('clock');

      到您的 router.js 中。

      谢谢!

      2014年8月20日 16:43

  6. gasolin

    FYI 我创建了一个 Web 应用模板,它与 Rec Room 类似,但也支持 Chrome 应用和每次提交的 Lint 检查 https://webapplate.github.io/

    2014年8月24日 15:49

    1. Robert Nyman [编辑]

      非常酷!我已通过电子邮件与您联系,想进一步了解。

      2014年8月25日 00:19

  7. Martin

    EmberJS 团队目前正在开发一个与 Rec Room 具有类似目标的工具:http://www.ember-cli.com/

    使用 Ember CLI 创建 Rec Room 应该很容易。据我了解,相比之下,似乎只有 Brick 和 I18n.js 集成缺失。但这些可以使用 Bower 轻松添加。此外,他们使用 Broccoli 而不是 Grunt 作为资产管道,据说性能更好。

    将来是否更有意义地联合起来?这样您就可以站在(看似更大的)EmberJS 社区的肩膀上。

    2014年8月25日 00:27

    1. Robert Nyman [编辑]

      感谢您的意见!我会与 tofumatt 谈谈,我们会回复您。

      2014年8月25日 01:22

    2. tofumatt

      目前,Ember CLI 仍然处于“测试版”阶段,并且处于积极的开发中,因此今天使用它并不现实。文档本身指出:虽然可能令人兴奋,但这确实是一个正在开发中的项目,请自行承担风险。将来也许我们可以使用 ember-cli 并与他们进行更多集成,但目前Grunt 和 Yeoman 更加稳定。

      Ember 确实做了很多我想让 Rec Room 能够做的事情,但我现在愿意使用更有效的东西,而不是使用可能更符合习惯的尖端 Ember 功能。

      当然,随着更多的人为 Rec Room 做出贡献和使用 Rec Room,我们将评估更换组件。如果我选择了错误的东西,我希望人们能够纠正它并使它变得更好,并且将来我当然希望做出更多贡献,使 Rec Room 尽可能接近 Ember 堆栈,以便我们能够共同解决问题。我们已经与 Ember 的人员简要讨论过这个问题,并且将来我认为使用 Ember CLI 将是 Rec Room 的做法,但现在还不是时候 ^_^

      2014年8月26日 10:29

  8. Adam

    Ember.JS 对 Firefox OS 来说是不是太臃肿了?我更喜欢简洁的 Riot.JS。
    Brick 与 Firefox OS Building Blocks 相比如何?

    2014年8月28日 15:01

  9. maitreya

    我似乎无法完成初始时钟控制器的设置。教程和我的之间有一些
    差异。在我的 clock_controller.js 中,我没有 WorldClock.ClockController,而是有 App.ClockController。

    不确定为什么会发生这种情况。哦,我还在控制台中注意到这两个错误
    “The route clock was not found” ember.js:3285
    “The URL ‘/clock.index’ did not match any routes in your application

    如果有人能帮我看看,我将不胜感激。

    谢谢!

    2014年9月6日 10:44

本文的评论已关闭。