李 紅
(東北財經(jīng)大學(xué)管理科學(xué)與工程學(xué)院,遼寧大連116025)
在如今的互聯(lián)網(wǎng)時代,基于移動端的Web應(yīng)用開發(fā)越來越受到重視.人們希望可以通過手機(jī)端查看所有他們需要完成的工作.可是瀏覽器和服務(wù)器端之間頻繁的信息交互使得移動端的Web應(yīng)用嚴(yán)重依賴網(wǎng)絡(luò),而且一旦網(wǎng)絡(luò)信號丟失,用戶在手機(jī)端的操作將全部丟失.面對這個亟需解決的挑戰(zhàn),一項新的技術(shù)應(yīng)運(yùn)而生,即Single Page Application(SPA,單頁應(yīng)用).
SPA的好處是極大地減少了和服務(wù)器的數(shù)據(jù)通信.即使沒有網(wǎng)絡(luò),用戶同樣可以在客戶端執(zhí)行操作,甚至可以暫時存儲數(shù)據(jù)在本地,等待有網(wǎng)絡(luò)時再與服務(wù)器同步.本文研究的內(nèi)容是實(shí)現(xiàn)SPA的主流Javascript框架,分析比較它們的優(yōu)缺點(diǎn),并給出選擇框架的建議.
傳統(tǒng)的基于服務(wù)端的Web應(yīng)用的運(yùn)行機(jī)制是:首先客戶端通過URL調(diào)用服務(wù)器端的資源,然后服務(wù)器端進(jìn)行數(shù)據(jù)組裝,返回給客戶端HTML頁面,客戶端的所有操作都將通過表單方式傳遞給服務(wù)器端,圖1是傳統(tǒng)Web應(yīng)用與SPA應(yīng)用的對比.
AngularJS[1],Backbone[2],Ember[3]有很多共同點(diǎn),比如,它們都是開源的,具有MIT開源許可,還有更重要的一點(diǎn),它們都在幫助開發(fā)者創(chuàng)建單頁Web應(yīng)用.它們的設(shè)計都是基于視圖、事件、數(shù)據(jù)模型和路由等概念.在此將先對 AngularJS[1,4],Backbong[2,5],Ember[3,6]3 種框架的背景和歷史進(jìn)行介紹,然后再對其進(jìn)行深入比較與分析.
AngularJS[4]是在 2009年開發(fā)一個較大的商業(yè)項目GetAngular時被開發(fā)出來的.不久后,GetAngular項目的一個開發(fā)人員Misko Hevery,對一個包含17 000行代碼、開發(fā)時間持續(xù)6個月的項目用了僅僅3個星期進(jìn)行了改寫.改寫后的代碼量減少到了1 000行代碼.這個舉動使得Google開始對該項目進(jìn)行贊助.這個項目就是今天看到的AngularJS.AngularJS的主要特性包括:雙向數(shù)據(jù)綁定、依賴注入、容易測試以及通過directive擴(kuò)展HTML標(biāo)簽.
BackboneJS[5]是一個輕量級的MVC框架.2010年開始,越來越多的開發(fā)者開始選擇Backbone,從而替代功能強(qiáng)大,但開發(fā)繁瑣的 MVC框架 ExtJS.使用者包括 Pinterest,F(xiàn)lixster,AirBNB,Twitter,F(xiàn)oursquare,LinkedIn 等公司.
Ember[6]的歷史可以追溯到2007年.它的前身是SproutCore MVC框架,它是由Sprout開發(fā)出來,后來由蘋果公司維護(hù).2011年,一位JQuery的核心開發(fā)者Yehuda Katz,在SproutCore MVC的基礎(chǔ)上對其改進(jìn),于是Ember被開發(fā)出來.Ember的使用者也有很多,其中較著名的公司包括:雅虎,LivingSocial,Groupon,Zendesk,Discourse,Square等.Ember的開發(fā)者 Tom Dale和 Yehuda Katz自信地說過,使用 Ember的web應(yīng)用的加載速度一定是很快的.
開發(fā)者在選擇開發(fā)框架時一般需要考慮的主要因素主要包括:開發(fā)社區(qū)的規(guī)模、框架的大小和框架生成模版的方式.下面就從這3個方面來比較AngularJS、Backbone和Ember框架.
開發(fā)社區(qū)的規(guī)模是開發(fā)者選擇一個開發(fā)框架的主要考慮因素.所謂開發(fā)社區(qū)是指針對開發(fā)人員的網(wǎng)上所有資源.因?yàn)橐粋€龐大的開發(fā)社區(qū)意味著,如果你在開發(fā)時遇到問題,那么很有可能別人已經(jīng)遇到過,而且可以找到解決方案.更重要的是開發(fā)者可以找到很多的第三方的幫助插件,還有很多的學(xué)習(xí)文檔和資料.筆者從Github關(guān)注人數(shù)、第三方模塊數(shù)量、StackOverflow問題數(shù)量、教學(xué)視頻、GitHub貢獻(xiàn)者以及Chrome插件人數(shù)6個方面總結(jié)了截至到2015年6月30日3個框架的開發(fā)社區(qū)規(guī)模的統(tǒng)計情況,如表1所示.可以看出,AngularJS總體來說更受到開發(fā)者的歡迎.
表1 開發(fā)社區(qū)規(guī)模的統(tǒng)計(1 k=1 000個)
在關(guān)注開發(fā)社區(qū)規(guī)模的同時還要注意所使用的框架在開發(fā)時可能遇到的問題.表2統(tǒng)計了Stack-Overflow上關(guān)于3個框架的問題數(shù)量.從開放問題以及已解決的問題數(shù)量進(jìn)行統(tǒng)計,可以看出AngularJS的開放問題以及解決的問題都是最多的.這意味著AngularJS的開發(fā)者如果在開發(fā)時遇到問題,那么他們更容易找到解決方案.
表2 StackOverflow上問題的統(tǒng)計(1 k=1 000個)
表1和表2僅對目前的使用情況進(jìn)行了比較,為了比較這3個框架所受歡迎程度的增長速度,筆者還用Google Trends對其進(jìn)行了比較.圖2顯示了這3個框架被關(guān)注和使用的增長曲線.
隨著移動互聯(lián)網(wǎng)應(yīng)用的發(fā)展與普及,用戶對Web應(yīng)用的速度越來越挑剔,頁面的加載速度是開發(fā)者經(jīng)常遇到的問題,因此頁面加載速度對一個Web應(yīng)用的成功至關(guān)重要,而影響頁面加載速度的最主要的因素就是開發(fā)框架的大?。?/p>
在開發(fā)Web應(yīng)用時,開發(fā)者經(jīng)常對Javascript library進(jìn)行縮減和壓縮,所以筆者在表3.3中比較了這三個框架壓縮后的大?。牵瑑H僅關(guān)注框架本身大小還不夠.比如Backbone.js僅有6.5kb大小,但是它還需要其它第三方的library的依賴,比如Underscore.js(5kb),還有Jquery(32kb)或者Zepto(9.1kb).
表3 框架大小比較(1 kb=1 024 Byte)
AngularJS和Ember都包含了自動生成HTML模版的功能.但是Backbone讓開發(fā)者自己來選擇如何生成模版.比較框架自動生成HTML模版的不同的最好辦法就是通過代碼實(shí)例.所以下面將對這3個框架分別舉出一個例子來說明其不同.
2.3.1 AngularJS AngularJS是通過綁定表達(dá)式來生成HTML模版.綁定表達(dá)式位于兩個中括號之間.圖3展示了AngularJS的模版實(shí)例.
2.3.2 Backbone Backbone本身不支持生成HTML模版,開發(fā)者可以自行選擇.默認(rèn)的第三方Javascript Library是Underscore模版.當(dāng)Underscore作為依賴注入到backbone的Web應(yīng)用后,開發(fā)者就不需要其它的模版生成工具了.但是不足之處是由于Underscore提供的功能過于基礎(chǔ),開發(fā)者需要在模版中混合使用Javascript代碼.圖4顯示Backbone的模版實(shí)例.
2.3.3 Ember Ember使用Handlehars作為模版生成library.Handlehars是當(dāng)前主流的模版生成library-Mustache的一個擴(kuò)展.Handlehars不理解DOM,它只能通過簡單的字符串的轉(zhuǎn)換來生成HTML模版.圖5展示了Ember使用HTMLbars來打印出框架列表.圖5顯示Ember的模版實(shí)例.
3.1.1 優(yōu)點(diǎn) AngularJS給Web開發(fā)者帶來很多益處.雙向數(shù)據(jù)綁定幫助開發(fā)者省去了很多冗余代碼.下面筆者通過比較Jquery和AngularJS,來說明雙向數(shù)據(jù)綁定.圖6顯示了JQuery的實(shí)例代碼.
如果使用AngularJS,開發(fā)者不需要來寫這些Javascript代碼.圖7是AngularJS的實(shí)現(xiàn)方式.
比起其它兩個框架,AngularJS不僅擁有最大的開發(fā)社區(qū),它還擁有一個強(qiáng)大的后盾,即Google.正因?yàn)橛?Google的技術(shù)團(tuán)隊的支持,很多創(chuàng)新和工具,比如,Protractor,batarang,ngmin,還有 Zone.js等,被不斷地創(chuàng)造出來,提高開發(fā)者的開發(fā)效率.而且AngularJS的開發(fā)團(tuán)隊與社區(qū)一起合作.例如,AngularJS 2.0的所有資料都可以在線上找到,并且每個人都可以給出自己的建議或意見.
AngularJS對Web應(yīng)用進(jìn)行了歸類.每個Web的應(yīng)用都離不開如下幾個類別:Controllers,Directives,F(xiàn)actories,F(xiàn)ilters,Services和Views.每個Module都可以通過依賴注入來調(diào)用不同類別.每個類別都有自己的角色.Views負(fù)責(zé)UI,Controllers負(fù)責(zé)UI背后的業(yè)務(wù)邏輯,Services負(fù)責(zé)與后臺服務(wù)的溝通,Directives可以方便開發(fā)者創(chuàng)建能夠被重復(fù)使用的功能模塊以節(jié)省開發(fā)時間.
從圖8中可以看到AngularJS與傳統(tǒng)JavaScript框架在處理DOM模板方式的不同.傳統(tǒng)JavaScript的處理模版流程如下所示:帶有標(biāo)記的模版 ->框架模版引擎 ->HTML->DOM但是,AngularJS直接把標(biāo)記放進(jìn)HTML里,模版生成流程如下所示:帶有標(biāo)記的HTML->DOM->AngularJS模版引擎
比較可以看出,AngularJS是在HTML全部加載進(jìn)DOM后才開始處理HTML標(biāo)記.這樣做的好處有3點(diǎn):
(1)容易與現(xiàn)有應(yīng)用程序集成.這是因?yàn)锳ngularJS是在整個HTML加載進(jìn)DOM后才發(fā)揮作用,程序開發(fā)者可以很容易引入AngularJS到現(xiàn)有web應(yīng)用程序中,而不需更改現(xiàn)有程序的邏輯和代碼.
(2)簡單易用.使用AngularJS開發(fā)web應(yīng)用不需要復(fù)雜的服務(wù)器部署,程序開發(fā)者可以在本地文件系統(tǒng)中創(chuàng)建需要項目,然后在瀏覽器中即可調(diào)試.如果需要跟后臺進(jìn)行交互,開發(fā)者可以使用AngularJS自帶的HTTP和Resource模塊與后臺進(jìn)行異步調(diào)用.
(3)可擴(kuò)展.程序開發(fā)者使用Directive[7]創(chuàng)建個性標(biāo)簽和屬性來擴(kuò)展HTML5自帶的標(biāo)簽.比如,在圖8中,開發(fā)者自定義了標(biāo)簽:my-custom-tag.開發(fā)者可以自定義這個標(biāo)簽的功能以及屬性.而且每個directive都有自己獨(dú)立的scope(isolated scope).這意味著在directive內(nèi)部的scope是與它上級的scope完全區(qū)分開的,所以開發(fā)者可以自定義directive內(nèi)部scope所需要完成的功能.如果需要與上級scope溝通,可以自定義directive的屬性.總之,directive極大地擴(kuò)展了現(xiàn)有HTML5提供的標(biāo)準(zhǔn)標(biāo)簽,使得程序開發(fā)變得容易多樣.
依賴注入(Dependency Injection,DI[8])是AngularJS提供的另外一個特別重要的開發(fā)模式.從圖9中可以看到changeUrlTo作為一個獨(dú)立的模塊(factory)被注入到myCtrl中,在myCtrl中可以調(diào)用changeUrlTo來實(shí)現(xiàn)頁面跳轉(zhuǎn)的功能.這樣的例子還有很多,好處就是開發(fā)者可以獨(dú)立定義每個功能模塊,然后依賴注入到其它的模塊中.這個開發(fā)模式對團(tuán)隊開發(fā)尤其重要,因?yàn)閳F(tuán)隊中每個人都可以開發(fā)自己的模塊,如需要復(fù)用現(xiàn)有模塊,團(tuán)隊成員可以很方便地把現(xiàn)有模塊依賴注入到他們開發(fā)的模塊中去.
自動數(shù)據(jù)校驗(yàn)功能也是AngularJS的一個優(yōu)點(diǎn).開發(fā)者無需通過getter和setter來獲得數(shù)據(jù),取而代之的是,開發(fā)者可以調(diào)用Scope內(nèi)的對象,AngularJS可以自動探測到該對象的任何變化,并告訴關(guān)心該對象數(shù)據(jù)變化的其它對象.
AngularJS在設(shè)計時也充分地考慮到了測試的重要性.目前使用較多的就是如前文所說的Protractor[9].Protractor是針對AngularJS應(yīng)用的完整的測試框架.在執(zhí)行測試之前,開發(fā)者可以自定義需要測試的情景,執(zhí)行時,protractor會模仿用戶真實(shí)操作,打開瀏覽器,然后執(zhí)行之前定義好的動作.默認(rèn)測試結(jié)果是XML格式.可以通過安裝第三方差價,比如,protractor-html-screenshot-reporter[10],來生成html格式的測試結(jié)果報告.本文以angular-seed做為例子,下載地址:https://github.com/angular/angularseed.git.執(zhí)行protractor后的測試結(jié)果如圖10所示.
3.1.2 缺點(diǎn) AngularJS經(jīng)常由于其 Directive API[11]的復(fù)雜性而受到批評.尤其是Directive屬性Transclusion讓開發(fā)者不知所措.還有Directive的編譯過程、pre/post鏈接的方法以及isolated scope等概念都很復(fù)雜,開發(fā)者需要花費(fèi)很多時間來學(xué)習(xí).
在AngularJS里,Scope的級別使用了原形繼承這樣一個面向?qū)ο笤O(shè)計語言里的概念.很多開發(fā)者都無法理解isolated scope的用處以及是否可以簡化.
AngularJS表達(dá)式在視圖里經(jīng)常會被使用到.這個功能很有用,但有時超過了它應(yīng)有的功能.它可以讓開發(fā)者在頁面視圖層表達(dá)出復(fù)雜的業(yè)務(wù)邏輯,甚至進(jìn)行計算和數(shù)值賦值操作.但是將業(yè)務(wù)邏輯放到視圖層讓應(yīng)用很難進(jìn)行測試.圖11顯示的是AngularJS復(fù)雜表達(dá)式實(shí)例代碼.
很多情況下,Directive的拼寫錯誤或者調(diào)用沒有定義的scope方法都會在程序運(yùn)行時報錯.而且往往這些錯誤還和很多其它復(fù)雜方法混合在一起,使得開發(fā)者經(jīng)?;ㄙM(fèi)很多時間來糾錯.開發(fā)者甚至花費(fèi)幾個小時的時間,最后發(fā)現(xiàn)他沒有按照AngularJS directive要求的用‘-’鏈接參數(shù)名稱,而使用了首字母大寫的方式定義directive的參數(shù).
最后,AngularJS digest循環(huán)解決了很多數(shù)據(jù)校驗(yàn)工作,這也是很多開發(fā)者喜歡AngularJS的原因之一.但是開發(fā)經(jīng)常在運(yùn)行非AngularJS的數(shù)據(jù)操作后,忘記調(diào)用$digest(),致使開發(fā)者無法找到錯誤.而且另外一方面,開發(fā)者還需要注意使用watch表達(dá),因?yàn)檫@樣可能在復(fù)雜的應(yīng)用里降低運(yùn)行速度.一般在相同的頁面里的數(shù)據(jù)綁定要少于2 000個,否則運(yùn)行效率會大大降低.
3.2.1 優(yōu)點(diǎn) Backbone[12]是一個輕量級的、快速的、占用內(nèi)存小的框架.它的學(xué)習(xí)曲線是一個線性的,而且它只有幾個簡單的概念需要掌握,例如,Modes、Collections[13]、views、Routes.它還有很多豐富的文檔,而且其代碼也非常簡潔.有經(jīng)驗(yàn)的開發(fā)者甚至可以查看它所有的原代碼,在一個小時內(nèi)可以了解它的全部實(shí)現(xiàn)細(xì)節(jié).
正因?yàn)锽ackbone提供的功能如此基礎(chǔ),所以它很適合開發(fā)者在其基礎(chǔ)上進(jìn)行二次開發(fā)來做出適合自己的框架.在 Backbone基礎(chǔ)上開發(fā)出來的框架包括:Aura,Backbone UI,Chaplin,Geppetto,Marionette,LayoutManager,Thorax,Vertebrae.但是如果使用AngularJS和Ember,開發(fā)者沒有其它的選擇.所以Backbone非常適合那些需要二次開發(fā)的使用者.AngularJS已經(jīng)做出保證,在2.0版本里將改變這一問題.
3.2.2 缺點(diǎn) Backbone沒有提供完整開發(fā)結(jié)構(gòu).它僅提供基礎(chǔ)開發(fā)工具,但是整體開發(fā)結(jié)構(gòu)的其它部分需要由開發(fā)者自行選擇和配置.在內(nèi)存管理方面,開發(fā)者需要仔細(xì)考慮.對視圖生命周期的管理如果沒有仔細(xì)地檢測,很容易導(dǎo)致內(nèi)存泄露.
盡管Backbone允許開發(fā)者自行選擇第三方插件來完成web應(yīng)用的開發(fā),但現(xiàn)實(shí)情況是有太多的可以選用的插件.比如,嵌套模版這一項功能的可選插件就包括:Backbone.DocumentModel[14],BackBone.NestedTypes,Backbone.Schema,Backbone-Nested,backbone-nestify 等.選擇哪個最適合自己的項目需要開發(fā)者花費(fèi)很多時間進(jìn)行研究與比較.
Backbone沒有提供雙向數(shù)據(jù)綁定功能.這也就意味著開發(fā)者需要寫很多Javascript代碼來實(shí)現(xiàn)更新視圖的操作.在上面的例子中可以看到,AngularJS的雙向數(shù)據(jù)綁定功能節(jié)省了很多不必要的代碼.
Backbone是對HTML視圖直接操作的,使得測試工作非常難做.
3.3.1 優(yōu)點(diǎn) Ember的設(shè)計原則是慣例優(yōu)先原則.這意味著開發(fā)者無需寫很多模版代碼,Ember會自動識別出代碼配置,例如,自動監(jiān)測路由名稱,以及對應(yīng)的Controller.如果開發(fā)者忘記創(chuàng)建,Ember甚至可以幫助開發(fā)者創(chuàng)建Controller.
Ember包含了一個極好的路由機(jī)制,同時也包含了一個數(shù)據(jù)層ember data.不同于其它兩個框架(Backhone Collection/Model,Angular JS$resource),Ember的數(shù)據(jù)層可以近乎完美地與Ruby-on-Rails后臺或其它RESTful JSON API進(jìn)行集成.
性能也是Ember設(shè)計時的主要考慮因素.Run loop機(jī)制可以使得數(shù)據(jù)永遠(yuǎn)在一個DOM里面進(jìn)行更新,無論這項數(shù)據(jù)被更新了多少次.還有在編譯代碼或者在服務(wù)器端提前編譯handlebars模版時,確保程序運(yùn)行速度與效率.
3.3.2 缺點(diǎn) Ember API從其創(chuàng)建以來變化很多.有很多過時的內(nèi)容和例子,使得開發(fā)者感到無所適從.而且有很多使程序不可運(yùn)行的改變,不過仔細(xì)研究后可以在網(wǎng)上找到相應(yīng)的答案.
Handlebars[15]對 DOM 的操作通過嵌套很多<script>標(biāo)簽.這些標(biāo)簽的作用是使得對象數(shù)值能夠與模版中顯示的數(shù)值同步.但是這會嚴(yán)重影響程序代碼的可讀性,而且它也會破環(huán)CSS樣式和一些其它第三方library的集成,比如,Jquery UI Sortable.
喜歡Ember的開發(fā)者經(jīng)常把Ember與Rails進(jìn)行對比,并仿造Rails來簡化開發(fā)難度與復(fù)雜度.Rails的確可以很大程度減少程序開發(fā)的工作量,因?yàn)樗梢宰詣犹幚砗芏嚅_發(fā)過程中必要的功能,比如數(shù)據(jù)持久化,約定由于配置等.可是從圖12中可以看出Ember中涉及的層次將近Rails的兩倍還多.這種復(fù)雜程度無疑增加了程序開發(fā)者的學(xué)習(xí)和開發(fā)時間.
上文總結(jié)了3個框架的優(yōu)點(diǎn)以及缺點(diǎn).但是,因?yàn)轫椖康男枨蟛煌?、程序開發(fā)者的個人喜好和經(jīng)驗(yàn)不同,所以,在決定使用哪種框架時要具體情況具體分析.
由于Ember是建立在MVC基礎(chǔ)上,所以它非常適合具有MVC開發(fā)背景的開發(fā)人員,比如,具有Ruby,Python,Java,C#等面向?qū)ο箝_發(fā)經(jīng)驗(yàn)的開發(fā)人員.Ember的設(shè)計也考慮到了應(yīng)用程序的運(yùn)行效率的問題,也可按慣例優(yōu)先讓開發(fā)者省去了很多不必要的開發(fā)工作.
Backbone以其精小而著稱.它的源代碼非常小,運(yùn)行速度快,學(xué)習(xí)門檻低,而且開發(fā)者可以根據(jù)自己的需求進(jìn)行更改和二次開發(fā).
AngularJS的創(chuàng)新使得web開發(fā)者很容易對HTML進(jìn)行擴(kuò)展.而且擁有一個龐大的開發(fā)社區(qū)和Google的支持.它既適合快速地開發(fā)小型項目,也可以應(yīng)用在大型項目中,通過創(chuàng)建可重復(fù)使用的directives來節(jié)省開發(fā)成本.
[1]Jain,Nilesh,Priyanka Mangal,Deepak Mehta.AngularJS:AModern MVC Framework in JavaScript[J].Journalof Global Research in Computer Science,2015,5(12):17-23.
[2]Fink,Gil,IdoFlatow.Getting Started with Backbone.js[J].Pro Single Page Application Development,2014,2:69-98.
[3]Skeie,Joachim Haagen.Ember.js in Action[M].US:Manning Publications Co,2014.
[4]Seshadri,Shyam,Brad Green.AngularJS:Up and Running:Enhanced Productivity with Structured Web Apps[M].US:O’Reilly Media,Inc,2014.
[5]Walker,Jeremy.Backbone.js Essentials[M].US:O’Reilly Media,Inc,2015.
[6]Kelonye,Mitchel.Mastering Ember.js[M].US:Packt Publishing Ltd,2014.
[7]Freeman,Adam.Creating Custom Directives[J].Pro AngularJS,2014,5:381-412.
[8]Freeman,Adam.Using Element and Event Directives[J].Pro AngularJS,2014,8:263-283.
[9]Kozlowski,Pawel.MasteringWeb Application Developmentwith AngularJS[M].US:Packt Publishing Ltd,2013.
[10]Resig,John,Russ Ferguson,et al.AngularJSand Testing[J].Pro JavaScript Techniques,2015,125-140.
[11]Knol,Alex.Dependency Injection with AngularJS[M].US:Packt Publishing Ltd,2013.
[12]Fink,Gil,IdoFlatow.Pro Single Page Application Development:Using Backbone.js and ASP[M].US:NET Apress,2014.
[13]Armeli-Battana,Sebastiano.MVC Applies to JavaScript[M].US:Developer Press,2013.
[14]Mulder,Patrick.Full Stack Web Developmentwith Backbone.js[M].US:O’Reilly Media,Inc,2014.
[15]Bodmer,Marc.Instant Ember.js Application Development How-to[M].US:Packt Publishing Ltd,2013.