在开始Ember.js时,Rails或服务器端开发人员应该采用什么范式转变?

时间:2013-04-09 21:57:36

标签: javascript ruby-on-rails model-view-controller ember.js

虽然我最近的大部分工作主要是使用Ruby on Rails和大量的Javascript(主要是jQuery),但我想构建一个单页应用程序并意识到Ember.js似乎是一个up-and-即将到来的流行框架,用于处理此类应用程序。

从各种文档和教程来源,似乎Ember.js需要一种与Ruby on Rails或其他典型服务器端框架相比,如何解决问题的方式。似乎有可能使用像Ruby on Rails这样的框架随着时间的推移开发的“事物应该如何运作”的某些假设甚至可能妨碍真正理解和拥抱'Ember Way'。

在尝试学习Ember时,Ruby on Rails开发人员需要消除哪些先入为主的观念? Ruby on Rails开发人员应该尝试将他/她的思想包裹起来的最具创新性和最重要的Ember概念是什么?

提前致谢!

1 个答案:

答案 0 :(得分:13)

我将通过列出Ember和Rails之间的一些主要技术差异,尽力在StackOverflow的精神内回答这个问题。我会在programmers.stackexchange.com为其他人留下更为哲学的一面。

您可以在working jsFiddle中找到以下所有代码示例,如果这有助于您了解所有内容如何组合在一起。

集合和对象的单独路由

Ember和Rails之间的一个主要区别是收集路由(管理对象列表)和项路由(管理单个对象)之间的关系。在Rails中,这些都由单个资源控制器处理。在Ember中,这些通常由两个不同的路径处理,因为它们操纵两个不同的数据结构:

App.Router.map(function () {
  this.route("posts", { path: "posts" });
  this.route("post", { path: "post/:post_id" });
});

App.PostsRoute = Ember.Route.extend({
  model: function (params) {
    return App.Post.find();  
  }
});

App.PostRoute = Ember.Route.extend({
  model: function (params) {
    return App.Post.find(params.post_id);  
  }
});

路由与控制器对比视图与模板

在Rails中,您的代码分为三大类:

  • 模型:数据库行上面向对象的抽象。
  • 视图:可由控制器呈现的模板。
  • 控制器:接受HTTP请求,加载和操作模型,渲染视图。

在恩伯,责任细分有很大不同。

模型。 Ember模型与Rails模型非常相似。

App.Post = DS.Model.extend({
  title: DS.attr("string"),
  body: DS.attr("string"),
  comments: DS.hasMany("App.Comment")
});

路线。路线代表应用中用户可见的位置,并且与/post/7/about等网址相对应。正如您在上面的代码示例中所看到的,路由在Ember中做了很多。最重要的是,他们查找与给定URL对应的模型。他们还负责连接适当的控制器和视图,您将在一秒钟内看到。

控制器。控制器与Rails完全不同!关于Ember控制器的两个最重要的事情是:(1)它们基本上是模型对象周围的智能代理,(2)它们通常是单例。因此,您只需要一个PostController即可连接到您现在正在查看的任何帖子。

一般来说,您使用Ember控制器来管理不属于URL或数据库的瞬态。这是一个例子:

App.PostController = Ember.ObjectController.extend({
  // This shared between all posts for as long as the app runs (because
  // this controller is a singleton), but it doesn't get saved in the database
  // (because this is a controller, not a model).
  lowRatedCommentsShown: false,

  // Called by PostView or its template in response to an HTML event.
  showLowRatedComments: function () {
    this.set("lowRatedCommentsShown", true);
  },

  // Called by PostView or its template in response to an HTML event.
  hideLowRatedComments: function () {
    this.set("lowRatedCommentsShown", false);
  } 
});

因为Ember控制器是模型周围的代理,它们也倾向于累积几乎属于模型的逻辑,但感觉与应用中的特定屏幕关系太紧密。

视图和模板。 Ember视图和模板协同工作。最好将它们视为GUI小部件。

App.PostView = Ember.View.extend({
  // This can be omitted when we're created by a route.
  templateName: 'post'

  // Any HTML event handlers would go here if we needed them.  Our job is to
  // map between HTML events and events understood by the controller.
  //doubleClick: function (evt) {
  //  // We'll actually bind this to a specific button, not a click event.
  //  this.get("controller").send("showLowRatedComments");
  //}
});

我们的帖子模板可以自由混合模型定义的字段和控制器定义的字段:

<script type="text/x-handlebars" data-template-name="post">
  <h2>{{title}}</h2>

  <div class="body">{{body}}</div>

  {{#if lowRatedCommentsShown}}
    <button {{action 'hideLowRatedComments'}}>Hide Low-Rated Comments</button>
  {{else}}
    <button {{action 'showLowRatedComments'}}>Show Low-Rated Comments</button>
  {{/if}}

  {{partial "comments"}}
</script>

请注意,随着模型或控制器上的字段发生变化,视图将自动重新呈现需要更新的HTML部分!

异步行为,计算属性和绑定

因为Ember.js在浏览器中运行,所以许多操作都是异步的。 Ember的许多基本设计都基于使异步更新变得轻松愉快。这种情况的一个关键因素是对象异步加载。当您致电find时,您将获得一个卸载的对象:

post = App.Post.find(params.post_id)
post.get("isLoaded"); // -> false
post.get("title");    // -> not yet available

当服务器向您发送数据时,您会看到:

post.get("isLoaded"); // -> true
post.get("title");    // -> "Post #1"

为了帮助实现这一目标,Ember非常依赖computed propertiesobserversbindings。在每种情况下,关键的想法是数据的更改应该自动波及整个系统。例如,我们可以使用计算属性来确保isLowRated在评论rating发生变化时更新:

App.Comment = DS.Model.extend({
  post: DS.belongsTo("App.Post"),
  body: DS.attr("string"),
  rating: DS.attr("number"),

  isLowRated: function () {
    return this.get("rating") < 2;
  }.property("rating") // A list of properties we depend on.
});

请注意,Ember的Handlebars模板与此系统深度集成。当您在模板中编写{{title}}时,您将建立一个绑定,以便在title更改时自动更新DOM。在编辑字段的情况下,此绑定在两个方向上都有效!对显示值的更改将直接推送回模型(尽管可以使用事务将其回滚)。

还值得记住的是,许多动态更新 - 特别是绑定 - 在当前&#34;运行循环结束时异步运行&#34;。这就是为什么您经常会在测试套件中看到Ember.run的来电:

Ember.run(function () {
  // Change some bindings here. Not all changes will propagate immediately.
});
// Test that the values have all propagated here, after the run loop is done.

在实践中,Ember比Rails感觉更异步,但是比Node.js这样的事件系统更少异步。这是因为大多数异步更新都是通过绑定自动管理的。

幸存的灰烬数据

这是我将从严格的技术细节中偏离并提及一些实用建议的地方。 Ember Data提供DS.Model,如上所示。它不是Ember.js的唯一模型层 - 请查看ember-restember-resource和类似的替代库。目前还没有Ember Data的官方发布,但是如果你想生活在最前沿,它可以在生产应用程序中非常谨慎地使用 。一些提示:

  1. 几个主要的弱点包括验证,延迟加载和多个开放交易。在提交Ember Data之前,请编写几个小的测试程序,以确保它可以实际执行您需要它做的事情。
  2. 不要选择使用RESTAdapter进行战斗。正确地输入the JSON it wants,即使这意味着构建代理。特别是,这当前意味着在序列化对象时序列化Rails中每个hasMany关系的完整ID列表。如果您正在使用Rails,请参阅active_model_serializers
  3. 不要过于关注特定的设计。相反,要做好准备偶尔解决限制并妥协。
  4. 使用Ember Data可以获得非常好的结果。但它远不如ActiveModel成熟,需要对其进行处理。

相关问题