Discourse的推出在整個社群賺足了眼球。Discourse選擇Ember.JS作為前端MVC架構,其開發者Robin Ward 寫了部落格分享選擇Ember.js的理由 最近Quora網站上也有人提問, Angular.js 和 Ember.js
,哪個JavaScript架構更好?
這個問題得到了熱烈的回應,兩個架構的開發者都參與了。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5SO1IDOyQGOiRmNllDOlFGOyUTM0IDNiV2YyczN0MTYi9CX5d2bs92Yl1iclB3bsVmdlR2LcNWaw9CXt92Yu4GZjlGbh5yYjV3Lc9CX6MHc0RHaiojIsJye.png)
Angular.js 擁抱 HTML/CSS
Misko Hevery(Angular.js的開發者之一)回答了這一問題,他的主要觀點如下:
在HTML中加入太多邏輯不是好做法。Angular.js隻放置綁定,而不是邏輯,建議把邏輯放入控制器中。但綁定同樣是資訊,通常,這些資訊可以放在三個地方:
- 代碼。但這使得程式子產品化很成問題,因為HTML與代碼緊密耦合,要想重新組成一個應用程式非常困難。
- HTML。這正是Angular.js所做的。除了放置綁定資訊外,你不應該在HTML中做任何事情。任何邏輯都不應該放在這裡,否則會導緻各種問題。我認為Angular.js做的綁定相當好。
- 中繼資料檔案:不知道是否有人這樣做,如果這麼做,就産生了一個新問題,你需要在代碼中把HTML和中繼資料結合起來。
Angular.js的獨特之處在于它擁抱HTML/CSS,其他一些架構提供了它們自己的API,偏離了HTML。Angular.js在所有架構中是能展現聲明式程式設計範式的。聲明式程式設計非常适合用來編寫使用者界面,編寫邏輯則交給JavaScript。
Angular.js允許你擴充HTML,是以你在使用Angular.js過程中遇到的任何問題都可以很容易地克服。
Ember.js 更社群化、更适合生産環境
Tom Dale(Ember.js的開發者之一)仔細比較了Angular.js和Ember.js.
Ember.js 由來
Dale首先來介紹了Ember.js項目的由來。從2009年開始,我就一直在蘋果公司參與 SproutCore 項目的開發,SproutCore 是一個 類似Cocoa的JavaScript開源架構,後來演變成了
iCloud。當時,我身邊是一些世界上最好的Cocoa開發者。
問題在于,用戶端應用程式這麼多年來似乎并沒有真正新的突破。自80年代以來就一直遵循的基本模型——代碼運作在本地計算機上,從網絡上擷取資料,然後在本地處理,并顯示在螢幕上;而如今唯一改變的是——代碼運作在浏覽器的沙箱環境中,然後加載所需的“二進制”檔案,而不是由使用者安裝到硬碟上的檔案。
在考慮這些問題時,我首先去想,在我們之前,人們已經做了什麼?我認為架構的作用無需争辯。比如Cocoa,無論在Mac還是iOS上,Cocoa都可以讓開發者輕松編寫受使用者喜愛的應用程式。
我們希望開發者能夠建立雄心勃勃的、能夠與本地應用競争的Web應用。要達成這一目标,開發者需要先進的工具和正确的理念。
Ember.js剛開始開發的時候,我們從Cocoa等本地應用程式架構引入一些概念,不過後來我們覺得這些概念弊大于利,或者說它們和Web應用程式格格不入。是以,我們開始從Ruby on Rails和Backbone.js 等開源項目中尋找靈感。
Ember.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的内部項目。
Ember.js 更社群化
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使用基于字元串的模闆有如下優勢:
- 模闆可以在伺服器上預編譯。這意味着減少啟動時間,也意味着渲染一個模闆可以像調用一個函數一樣簡單。而Angular.js需要你在應用程式啟動時周遊整個DOM,你的應用程式越大,啟動速度越慢。
- 如果你想在伺服器上渲染你的應用程式(友善搜尋引擎索引或讓首次加載時顯示速度更快),Angular.js需要啟動整個浏覽器環境,例如PhantomJS,這是資源密集型的。而Handlebars是100%的JavaScript字元串,所有你需要的隻是node.js或Rhino之類的東西。
- 如果你的應用程式變得越來越大,那麼字元串模闆可以很容易地分割和惰性加載。
此外,Handlebars隻讓你綁定屬性,而Angular.js允許你嵌入實時更新的任意表達式。剛開始很多人認為這是Ember.js的局限性,但實際上:
- 使用JavaScript來建立可計算屬性非常容易,它可以包含任意表達式。Ember.js隻要求你指定你的依賴,這樣在更新時可以智能些。
- 一旦有新的變化,Angular.js就必須重新計算這些表達式,這意味着需要在你的應用程式中綁定更多的元素,是以速度會變慢。
- 因為Ember.js隻允許你綁定屬性,我們将可以很容易地利用ECMAScript 6的性能優勢,如 Object.observes 。由于Angular.js發明了自己的帶有 自定義解析器 的JavaScript子集,這對于浏覽器來說,優化代碼變得比較困難。
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下一個版本的制定)的成員,在此方面相當有經驗。
Angular.js符合Web的未來
angularjs_scaffold的開發者Patrick Aljord也參與了讨論。
是基于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被優化得非常快,開發團隊通過如下措施來實作:
- 髒檢查
- 隻檢查目前視圖
- 隻在變化發生時檢查
- 通過和Chrome團隊協作來利用JIT
一些顯示Angular.js的速度要快于Ember.js,例如
Angular VS Knockout VS EmberAngular.js未來會擁有可複用的元件,這允許你編寫非常簡潔的代碼。這是Web的未來。
此外,Angular.js還擁有一個龐大的社群和
大量的貢獻者AngularJS 的缺陷
Discoures開發者
Evil Trout在自己的部落格上對比了這兩個架構。Evil Trout列舉了AngularJS的一些缺陷:
“簡單”的陷阱
現在我知道現在為什麼AngularJS勢頭越來越大:因為它很簡單。一個精簡了許多進階概念與實作的架構,會是以變得更容易學習。如果要我給這些架構排個名次的話,Angularjs大概是介于Backbone和Ember之間。
如果您的應用程式是簡單,那麼使用簡單的架構想來也是極好。但如果你是要建構大規模的應用程式的話就要謹慎選擇了,而且要進行長期的維護。
比起AngularJS,Ember有更多需要學習的概念。當你由于Ember的複雜性放棄它的時候,請考慮為什麼開發人員添加了這些所謂多餘的東西。事物的存在總有它的道理。
你會發現Ember是一個充滿概念與實用的工具集,如果你想建立一個龐大的、可維護的應用程式。它的API側重于通過一個健全的方式幫助你結構代碼。Ember有一些AngularJS架構沒有的理念。
AngularJS的Model層
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>{{area()}} sq ft.</p>
這顯示了Ember和AngularJS之間的關鍵差別。Ember遵循統一通路原則 。 在一個Ember模闆中,無論你所通路的是計算屬性還是primitive,表達方式看上去是一樣的。而在AngularJS中,必須明确區分函數。
這可能會導緻可維護性的惡夢。在一個大型軟體項目中,随着時間的推移,你将不可避免地要疊代原有的代碼。在Ember中,你可以輕而易舉地做到這點;而在AngularJS你就不得不更新每一個綁定于這個Model的模闆。
使用getter和setter
這個相關的權衡是值得讨論的,你可能已經注意到,在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連結到另一個路由:
{{#each user in users}}
<li>{{linkTo 'users.show' user}}Show {{username}}{{/linkTo}}</li>
{{/each}}
這裡,我們周遊一個使用者清單,并建立一個連結。當你的滑鼠懸停在連結上,如果你的路由配置正确,你會看到一些
/users/show/123
之類的文字。 然而,你點選連結時,Ember實際上通過你所配置的其他路由到達相關的使用者頁面。
Ember的路由器足夠聰明,如果這個使用者的id已經在記憶體中,Ember不會重複解析。而在AngularJS中,每次通路路由,它傳遞一個id并在Controller層進行解析。
一個長存的浏覽器應用程式的巨大優勢之一是可以重用的對象,就像上面那個使用者導航的例子。AngularJS并沒有遵循這一理念,它鼓勵你扔掉它,然後再次找到它(可能是從Server端再次擷取資料!)。