Discourse的推出在整个社区赚足了眼球。Discourse选择Ember.JS做为前端MVC框架,其开发者Robin Ward写了博客分享选择Ember.js的理由javascript
最近Quora网站上也有人提问,Angular.js和 Ember.js,哪一个JavaScript框架更好?php
这个问题获得了热烈的回应,两个框架的开发者都参与了。html
Misko Hevery(Angular.js的开发者之一)回答了这一问题,他的主要观点以下:前端
在HTML中加入太多逻辑不是好作法。Angular.js只放置绑定,而不是逻辑,建议把逻辑放入控制器中。但绑定一样是信息,一般,这些信息能够放在三个地方:java
Angular.js的独特之处在于它拥抱HTML/CSS,其余一些框架提供了它们本身的API,偏离了HTML。Angular.js在全部框架中是能体现声明式编程范式的。声明式编程很是适合用来编写用户界面,编写逻辑则交给JavaScript。node
Angular.js容许你扩展HTML,因此你在使用Angular.js过程当中遇到的任何问题均可以很容易地克服。git
Tom Dale(Ember.js的开发者之一)仔细比较了Angular.js和Ember.js.angularjs
Dale首先来介绍了Ember.js项目的由来。从2009年开始,我就一直在苹果公司参与 SproutCore 项目的开发,SproutCore 是一个 相似Cocoa的JavaScript开源框架,后来演变成了iCloud。当时,我身边是一些世界上最好的Cocoa开发者。github
问题在于,客户端应用程序这么多年来彷佛并无真正新的突破。自80年代以来就一直遵循的基本模型——代码运行在本地计算机上,从网络上获取数据,而后在本地处理,并显示在屏幕上;而现在惟一改变的是——代码运行在浏览器的沙箱环境中,而后加载所需的“二进制”文件,而不是由用户安装到硬盘上的文件。编程
在考虑这些问题时,我首先去想,在咱们以前,人们已经作了什么?我认为框架的做用无需争辩。好比Cocoa,不管在Mac仍是iOS上,Cocoa均可以让开发者轻松编写受用户喜好的应用程序。
咱们但愿开发者可以建立雄心勃勃的、可以与本地应用竞争的Web应用。要达成这一目标,开发者须要先进的工具和正确的理念。
Ember.js刚开始开发的时候,咱们从Cocoa等本地应用程序框架引入一些概念,不事后来咱们以为这些概念弊大于利,或者说它们和Web应用程序格格不入。所以,咱们开始从Ruby on Rails和Backbone.js 等开源项目中寻找灵感。
在Dale看来,与Ember.js相比,Angular.js更像一个研究项目。好比,在学习文档中,Ember.js主要讨论模型、视图和控制器,而Angular.js指南要求你去学习一些相似于范围、指示符和transclusion方面的内容等。
一些大公司已经在Ember.js上投入了大量时间和精力,好比ZenDesk对Backbone.js失望后使用Ember.js重写,Square的整个Web层面也是基于Ember.js的,Groupon的移动版Web应用也是使用Ember.js开发的。此外,还有不少创业公司经过Ember.js得到了成功,并开始回馈Ember.js社区。
而目前所看到使用Angular.js开发的大多数应用程序只是演示项目,或是Google的内部项目。
Yehuda(Ember.js开发者之一)和我也一直积极邀请真正的用户参与Ember.js框架的设计和维护,这能够确保咱们在Ember.js中添加的功能对于实际开发是有用的。
事实上,在过去的几个月中,大多数Ember.js开发工做都是由Ember.js社区的核心贡献组完成的,他们来自不一样的公司。若是Yehuda和我哪天有什么事情,或者咱们的公司倒闭了,Ember.js还将会持续发展。这是一个真正的社区项目,而不是“Google”项目。
Angular.js使用有语义意义的属性(好比data-ng-repeat)来实现模板。
而Ember.js使用Handlebars来描述HTML。
Handlebars语法(相似{{\#each}}
),和Angular.js那样使用额外的属性作法,哪一种更美观,是一个见仁见智的问题。我我的认为,HTML属性有点杂乱,可读性要差些。固然,若是Ember.js不存在,而我又必须使用一个使用了数据属性的框架,那么我会考虑Angular.js。
抛开美观不谈,我相信,Ember.js使用基于字符串的模板有以下优点:
此外,Handlebars只让你绑定属性,而Angular.js容许你嵌入实时更新的任意表达式。刚开始不少人认为这是Ember.js的局限性,但实际上:
Angular.js一般依靠一种叫作“dirty checking”的机制来肯定对象是否已进行更改。在你扫描每一个对象和其全部绑定属性时,比较当前值和以前已知的值。若是它发生了变化,就须要更新绑定。但Angular.js开发者很是聪明,使用“脏检查”,你不须要使用accessors。你能够用person.name = "Bill"
来代替person.set('name', "Bill")
,就像在Ember.js 或 Backbone.js中的同样。然而,使用“脏检查”,你没法一次有超过2000个绑定对象。
我认为这很好地说明了Ember.js 和 Angular.js理念上的区别。Ember.js 和 Angular.js都力求简单和易用。而Ember.js使你没必要担忧代码中是否有超过2000个绑定。若是你正在编写大型应用程序,那么你已经解决了你所担忧的最大的事情。对于中小规模的应用程序来讲,Angular.js一样是伟大的,由于这些应用程序不会触及Angular.js的限制区。
在Ember.js中,咱们老是但愿利用浏览器和语言中的新功能,以便使事情变得更容易。例如,一旦ES6中 代理对象(proxies)可用,咱们不会再要求你使用get()
和set()
。
因此这就是为何我认为——若是你想构建雄心勃勃的应用程序,你应该选择Ember.js。
此外,在开发过程当中,咱们对于性能方面和如何利用语言新特性方面也考虑了好久。Yehuda Katz和我一块儿开发Ember.js,他同时也是TC39(负责JavaScript下一个版本的制定)的成员,在此方面至关有经验。
angularjs_scaffold的开发者Patrick Aljord也参与了讨论。
angularjs_scaffold是基于Angular.js编写的针对scaffolding视图的Rails插件。
Patrick Aljord阐述了选择Angula.js的理由。
事实上,我本来打算在项目中使用Ember.js,由于我比较信赖Yehuda(Ember.js开发者之一),他在Rails和jQuery方面的工做很杰出。可是Ember.js中随时会变化的API和匮乏的文档,使我一再推迟使用它。偶然发现了Angular.js以后,我被它吸引了。
正如Tom Dale(Ember.js开发者之一)所说,Ember.js受到了Cocoa 和Rails启发。问题在于,在Ember.js下工做,我并无真正感受到像在写一个Web应用程序。而Angular.js让我感受像在写一个Web应用程序,它真正支持全部的Web概念,并以一种很是天然的方式来扩展HTML。
事实上,Angular.js并无使用本身的对象或重写JS方法,当你使用Angular.js时,你就使用了纯JS,而且Angular.js实现的许多概念都将直接进入下一个版本的Javascript中。
学习Angular.js,就意味着学习将来的Javascript,而学习Ember.js,你只是学习到了Ember的特有概念。
来看个例子。HTML是伟大的,由于它是声明式的,若是想要定义一个段落,你只需写以下代码:
<p>Hello world</p>
可是若是你想很是动态地实现?你须要经过相似于下面的代码来引导浏览器:
<p id="greeting1"></p> <script> var isIE = document.attachEvent; var addListener = isIE ? function(e, t, fn) { e.attachEvent('on' + t, fn);} : function(e, t, fn) { e.addEventListener(t, fn, false);}; addListener(document, 'load', function(){ var greeting = document.getElementById('greeting1'); if (isIE) { greeting.innerText = 'Hello World!'; } else { greeting.textContent = 'Hello World!'; } }); </script>
来看看Angular.js如何实现:
<p>{{hello}}</p>
再来看一个示例,若是你要遍历一个数组,只需:
<ul> <li ng-repeat="element in array">element</li> </ul>
这个语法看起来像新的 MDV标准。这看起来比Ember.js更加简洁。另外,Angular.js被优化得很是快,开发团队经过以下措施来实现:
一些显示Angular.js的速度要快于Ember.js,例如 Angular VS Knockout VS Ember。
Angular.js将来会拥有可复用的组件,这容许你编写很是简洁的代码。这是Web的将来。
此外,Angular.js还拥有一个庞大的社区和 大量的贡献者。
Discoures开发者Evil Trout在本身的博客上对比了这两个框架。Evil Trout列举了AngularJS的一些缺陷:
如今我知道如今为何AngularJS势头愈来愈大:由于它很简单。一个精简了许多高级概念与实现的框架,会所以变得更容易学习。若是要我给这些框架排个名次的话,Angularjs大概是介于Backbone和Ember之间。
若是您的应用程序是简单,那么使用简单的框架想来也是极好。但若是你是要构建大规模的应用程序的话就要谨慎选择了,并且要进行长期的维护。
比起AngularJS,Ember有更多须要学习的概念。当你因为Ember的复杂性放弃它的时候,请考虑为何开发人员添加了这些所谓多余的东西。事物的存在总有它的道理。
你会发现Ember是一个充满概念与实用的工具集,若是你想创建一个庞大的、可维护的应用程序。它的API侧重于经过一个健全的方式帮助你结构代码。Ember有一些AngularJS框架没有的理念。
AngularJS吹捧本身为MVC框架,或者是MVW(Model View Whatever)框架。
很明显的,AngularJS的View层是:让你经过ng-*
属性和handlebars风格的{{variable}}
表达式来标注HTML文档。Controller层是JavaScript类经过ng-controller
属性的元素绑定DOM元素。
特别是,若是你有一个Server端的MVC背景, AngularJS的Model层该是什么样的,这点并不明确。并且在AngularJS中,并无标准来定义了一个模型应该是Model基类,仍是一个component(组件)或interface(接口)。
在一个AngularJS的Controller层中,有一个$scope
对象。全部附加的数据经过它绑定在你的HTML模板:
function SomeCtrl($scope) { $scope.countries = ['can', 'usa', 'fra', 'jap']; $scope.user = {name: "Evil Trout"}; $scope.age = 34; // 咱们的模板如今能够渲染 {{age}}, {{user.name}} 和不少国家了! }
根据AngularJS的文档,在AngularJS中全部声明在$scope上
的对象都是一个Model,不只仅对象和数组是Model,连primitive也是!
在模板中,AngularJS提供给你所需的工具来管理单一数据来源。这是一个叫数据绑定的概念。若是咱们建立了一个模板中有一个AngularJS表达式{{age}}
,咱们说这绑定于$scope.age
这个Model。若是你在一个模板中多处书写{{age}}
,并在Controller层中执行 $scope.age = 40
,全部绑定的值都会同时更新。
然而,若是你真正想表达单一数据来源,你所须要的是在二级的数据绑定,就在你的数据Model自己。换句话说,AngularJS的短板在于:只容许将数据绑定在$scope和模板之间,而不是在Javascript代码的结构中。
在Ember中,全部Model扩展在 Ember.Object
基类上。所以你可以在Model内部声明对象之间的关系。例如:
App.Room = Ember.Object.extend({ area: function() { return this.get('width') * this.get('height'); }.property('width', 'height') });
在这里,咱们已经建立了一个名为Room
的Model。咱们已经声明area
为计算属性。property
通知Ember,Room
的area
属性取决于其width
和height
属性。
建立一个Room Model的实例仍是很容易的:
var room = App.Room.create({width: 10, height: 5});
如今咱们能够建立一个模板:
<p>Room:</p> <p>{{width}} ft.</p> <p>{{height}} ft.</p> <p>{{area}} sq ft.</p>
相应的Ember会正确地渲染这些属性。在这种状况下,area不得不与 width和 height同步更新。若是这两个属性的变化,area将自动更新。
由于在AngularJS中,Model都是普通的Javascript对象,AngularJS没有计算属性。可是,您能够在相应的对象上经过函数模拟来实现:
var Room = function(args) { this.width = args.width; this.height = args.height; } Room.prototype.area = function() { return this.width * this.height; }
要访问咱们的房间的面积,你必须添加一组括号area()调用:
<p>Room:</p> <p>{{width}} ft.</p> <p>{{height}} ft.</p> <p>{{area()}} sq ft.</p>
这显示了Ember和AngularJS之间的关键区别。Ember遵循统一访问原则 。 在一个Ember模板中,不管你所访问的是计算属性仍是primitive,表达方式看上去是同样的。而在AngularJS中,必须明确区分函数。
这可能会致使可维护性的恶梦。在一个大型软件项目中,随着时间的推移,你将不可避免地要迭代原有的代码。在Ember中,你能够垂手可得地作到这点;而在AngularJS你就不得不更新每个绑定于这个Model的模板。
这个相关的权衡是值得讨论的,你可能已经注意到,在Ember中,为了访问一个Model的属性,你必须使用getter
和setter
。这意味着须要一点点额外的代码,但你收获的是和模板中JavaScrip同样的好处:用函数替换primitive能够工做!
使用getter
和setter
的另外一个好处是能够保证安全。思考下面的代码:
console.log(room.inhabitant.name);
若是inhabitant
不存在,会发生什么事? 你会获得一个JavaScript错误。而在Ember中,你会获得undefined
返回值,这使得你更容易编写健壮的代码。
// 输出 undefined console.log(room.get('inhabitant.name'));
AngularJS相比Ember来讲更难复用对象实例。例如在Ember模板中,能够经过{{linkTo}}
helper连接到另外一个路由:
<ul> {{#each user in users}} <li>{{linkTo 'users.show' user}}Show {{username}}{{/linkTo}}</li> {{/each}} </ul>
这里,咱们遍历一个用户列表,并建立一个连接。当你的鼠标悬停在连接上,若是你的路由配置正确,你会看到一些/users/show/123
之类的文字。 然而,你点击连接时,Ember实际上经过你所配置的其余路由到达相关的用户页面。
Ember的路由器足够聪明,若是这个用户的id已经在内存中,Ember不会重复解析。而在AngularJS中,每次访问路由,它传递一个id并在Controller层进行解析。
一个长存的浏览器应用程序的巨大优点之一是能够重用的对象,就像上面那个用户导航的例子。AngularJS并无遵循这一理念,它鼓励你扔掉它,而后再次找到它(多是从Server端再次获取数据!)。
你是否曾经也纠结于Angular.js和Ember.js的选择,欢迎分享你的经验和心得。