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

Reb*_*ele 8 javascript model-view-controller ruby-on-rails ember.js

虽然我最近的大部分工作主要是使用Ruby on Rails和大量的Javascript(主要是jQuery),但我想构建一个单页应用程序,并意识到Ember.js似乎是一个崭露头角的流行框架接近这些应用程序.

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

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

提前致谢!

emk*_*emk 13

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

您可以在工作的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);  
  }
});
Run Code Online (Sandbox Code Playgroud)

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

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

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

在恩伯,责任的细分是截然不同的.

楷模.Ember模型的工作方式与Rails模型非常相似.

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

路线.路线表示应用中用户可见的位置,它们对应于/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);
  } 
});
Run Code Online (Sandbox Code Playgroud)

因为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");
  //}
});
Run Code Online (Sandbox Code Playgroud)

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

<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>
Run Code Online (Sandbox Code Playgroud)

请注意,当我们的模型或控制器上的字段发生更改时,视图将自动重新呈现需要更新的HTML部分!

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

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

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

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

post.get("isLoaded"); // -> true
post.get("title");    // -> "Post #1"
Run Code Online (Sandbox Code Playgroud)

为了帮助实现这一目标,Ember非常依赖于计算属性,观察者绑定.在每种情况下,关键的想法是数据的更改应该自动波及整个系统.例如,我们可以使用计算属性来确保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.
});
Run Code Online (Sandbox Code Playgroud)

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

还值得记住的是,许多动态更新 - 特别是绑定 - 在当前"运行循环"结束时异步运行.这就是为什么你经常会看到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.
Run Code Online (Sandbox Code Playgroud)

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

幸存的灰烬数据

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

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

使用Ember Data可以获得非常好的结果.但它远不如ActiveModel成熟,它需要被视为这样.