虽然我最近的大部分工作主要是使用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概念是什么?
提前致谢!
答案 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中,您的代码分为三大类:
在恩伯,责任细分有很大不同。
模型。 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 properties,observers和bindings。在每种情况下,关键的想法是数据的更改应该自动波及整个系统。例如,我们可以使用计算属性来确保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-rest,ember-resource和类似的替代库。目前还没有Ember Data的官方发布,但是如果你想生活在最前沿,它可以在生产应用程序中非常谨慎地使用 。一些提示:
hasMany
关系的完整ID列表。如果您正在使用Rails,请参阅active_model_serializers。使用Ember Data可以获得非常好的结果。但它远不如ActiveModel成熟,需要对其进行处理。