近年來隨著Web應(yīng)用交互復(fù)雜度的提升,前端開發(fā)也迎來了一個高速發(fā)展的時期。除了一些老牌框架紛紛推出改動較大的升級之外,還涌現(xiàn)出一批新生代的開源庫和框架,推動著Web應(yīng)用開發(fā)理念向越來越強(qiáng)調(diào)前端架構(gòu)的方向發(fā)展。當(dāng)下的前端技術(shù)可以說是處在一個新舊交替的過程之中,同時存在著許多不同的觀念和實(shí)踐。
本文試圖對目前數(shù)量繁多的前端框架進(jìn)行一些較籠統(tǒng)的分析和比較,拋磚引玉,希望能為大家在選擇前端的技術(shù)架構(gòu)時提供一些有益的參考。需要明確的是,本文探討的前端架構(gòu)是以JavaScript為主。有一些主要關(guān)注CSS層面的前端框架,如Bootstrap,不在本文的討論范圍之內(nèi)。
今天的JavaScript框架和庫繁多復(fù)雜,很大程度上源于Web前端開發(fā)本身的特殊性。從當(dāng)初的可有可無到今天各種功能完備的HTML5標(biāo) 準(zhǔn),JavaScript在Web應(yīng)用中的職責(zé)和定位經(jīng)歷了巨大的變化。加上長期以來各種瀏覽器對ECMA標(biāo)準(zhǔn)支持參差不齊的復(fù)雜環(huán)境,這導(dǎo)致大家對于 JavaScript能做什么、該做什么、應(yīng)該怎么做一直無法形成共識。一個Web應(yīng)用可以把所有業(yè)務(wù)邏輯全部放在服務(wù)器端,幾乎不依賴 JavaScript;也可以完全用JavaScript構(gòu)建客戶端,服務(wù)器只負(fù)責(zé)數(shù)據(jù)接口;更有可能選擇介于兩者之間的折中方案。整體架構(gòu)選擇的多樣性 使得不同的應(yīng)用對于前端架構(gòu)有著截然不同的需求。這意味著很難有一個前端庫或框架可以滿足所有人,也使得開發(fā)者在找不到完美方案的情況下選擇重復(fù)造輪子。 同時,由于JavaScript是一門相當(dāng)靈活的語言,不同背景的開發(fā)者借鑒了許多不同的軟件設(shè)計思想來構(gòu)建他們理想中的JavaScript框架,這也 導(dǎo)致不同的框架/庫在解決同一個問題時經(jīng)常有不同的方案,例如單頁應(yīng)用的設(shè)計模式問題。
框架vs.庫
眾所周知,在前端開發(fā)中對于庫(Library)和框架(Framework)的區(qū)分向來是有些模糊的。像jQuery、YUI這些項(xiàng)目的官方描述都是 “庫”,卻經(jīng)常在各種地方被人們稱作“框架”。近兩年出現(xiàn)的一些MVC項(xiàng)目號稱框架,實(shí)際上卻更像庫。此外,在同樣號稱框架的各個項(xiàng)目之間所覆蓋的功能也 都有所不同。傳統(tǒng)軟件工程對于庫和框架的區(qū)分主要著眼于對應(yīng)用運(yùn)行流程的控制權(quán)。框架提供架構(gòu),控制運(yùn)行流程,讓開發(fā)者在合適的地方書寫針對具體問題的代 碼;而庫則附屬于架構(gòu),不控制運(yùn)行流程,只提供可調(diào)用的函數(shù)。但由于上述Web前端開發(fā)的特殊性,這樣的定義顯得有些過于嚴(yán)格:真正稱得上框架的項(xiàng)目很 少,卻又經(jīng)常需要和作為庫的項(xiàng)目進(jìn)行比較。因此,在比較JavaScript開源項(xiàng)目時,是框架還是庫并不特別重要,首先應(yīng)該分析該項(xiàng)目覆蓋了前端開發(fā)中 的哪些問題(下文為了論述方便,一律用框架指代各類JavaScript開源項(xiàng)目)。
前端開發(fā)可能面對的需求
前端開發(fā)中最常見的問題大致可以分為:封裝原生API和常用任務(wù)、基礎(chǔ)架構(gòu)、富應(yīng)用架構(gòu)、視覺交互,以及工具鏈。下面我們逐個分析。
封裝原生API和常用任務(wù)
JavaScript的原生API存在以下問題。
因此早期的一些框架最主要的目標(biāo)就是把煩瑣的原生API和常用的任務(wù)封裝成更簡潔直觀的API,同時,在封裝過程中也處理了兼容性問題。jQuery就是解 決這一部分問題的典型方案。HTML5和ECMAScript 5標(biāo)準(zhǔn)的出臺使得這些問題有所好轉(zhuǎn)。隨著新標(biāo)準(zhǔn)的普及,將來對于這一部分功能的需求會逐漸減弱。通常來說,封裝的對象包含以下類別。
基礎(chǔ)架構(gòu)
這一部分通常是各類框架中比較底層的功能,決定了采用此框架的代碼是如何被組織到一起的。目標(biāo)是提高代碼的可維護(hù)性、可協(xié)作性和可測試性。
模塊管理:對大型的JavaScript項(xiàng)目來說,模塊化開發(fā)是必需的。有些框架只提供基本的模塊注冊機(jī)制以防止全局變量污染和沖突,而另一些則提供包括 模塊依賴解析、文件加載、壓縮打包的功能。目前JavaScript模塊管理有兩個互相競爭的標(biāo)準(zhǔn),一個是AMD(Asynchronous Module Definition),采用的框架有Dojo,單獨(dú)的模塊管理庫有RequireJS;另一個是CommonJS的模塊標(biāo)準(zhǔn),采用者有模塊管理庫 SeaJS,以及基于SeaJS的開放型框架Arale。面向?qū)ο?/span>:JavaScript有原型繼承,并且可以非常靈活地進(jìn)行動態(tài)混入,但很多大型項(xiàng)目還是需要一個統(tǒng)一的面向?qū)ο蟮睦^承/擴(kuò)展系統(tǒng)。對 此,John Resig、Douglas Crockford、Nicholas Zakas等各路JavaScript大神都曾經(jīng)進(jìn)行研究并給出過各自的解決方案,許多框架中也包含類似的解決方案。對此感興趣的同學(xué)推薦閱讀Arale 文檔中的這篇文章:http://aralejs.org/class/docs/competitors.html。
自定義事件系統(tǒng):為了提高模塊的可復(fù)用性、整個系統(tǒng)的容錯性和靈活性,各個模塊之間需要盡量解耦,使得相互之間盡可能減少依賴。要實(shí)現(xiàn)這樣的解耦,一個自定義的事件機(jī)制 (通常借鑒Pub-Sub、Observer、Mediator等設(shè)計模式) 是很好的手段。
組件系統(tǒng):定義如何使用和書寫組件、組件之間如何相互調(diào)用和通信等。
富應(yīng)用架構(gòu)
這一部分的主要目的是利用設(shè)計模式進(jìn)一步提高代碼復(fù)用,使得開發(fā)者的精力可以主要集中在實(shí)現(xiàn)應(yīng)用本身的功能上。
代碼邏輯分層:把對視覺界面、交互邏輯和數(shù)據(jù)的處理清晰地分開。就這一點(diǎn)而言,大部分框架借鑒了經(jīng)典的MVC模式,但傳統(tǒng)的MVC在前端并不適合直接套 用,因此各個框架對此的處理都略有不同,有些采用了MVP(Model-View-Presenter)或是MVVM(Model-View- ViewModel)模式。著名前端布道師Addy Osmani有詳細(xì)的分析,本文受篇幅所限不再贅述。
數(shù)據(jù)綁定:把界面和數(shù)據(jù)模型進(jìn)行綁定,使得一方變化的時候另一方也會自動變化,可以省去手動更新DOM的操作。
數(shù)據(jù)與服務(wù)器端的同步:服務(wù)器端提供符合REST規(guī)范的API,前端的數(shù)據(jù)模型可以封裝同步操作,可以省去手動發(fā)送Ajax請求的過程。
模板渲染:在前端存儲和渲染可復(fù)用的HTML模版,這樣更新界面時不需要再向服務(wù)器發(fā)送額外的請求。
URL路徑、應(yīng)用狀態(tài)和歷史管理:無論是從搜索引擎還是用戶體驗(yàn)的角度來看,大型單頁應(yīng)用都應(yīng)該提供和應(yīng)用的狀態(tài)相對應(yīng)的URL,同時不破壞后退鍵的功能。
視覺交互
傳統(tǒng)型大框架會包含這部分內(nèi)容,通常是基于自身架構(gòu)上的擴(kuò)展,對框架自身有依賴性。但一些新框架則只專注于架構(gòu),對擴(kuò)展部分徹底持開放態(tài)度,提倡讓開發(fā)者自己選擇最合適的工具。
效果和動畫:用原生JavaScript實(shí)現(xiàn)動畫是一個比較煩瑣的過程,尤其是當(dāng)需要精確的時間和緩動(easing)處理時。因此一些框架如jQuery提供一個API簡潔的動畫引擎,并封裝了常見的動畫效果。
UI組件庫:這是傳統(tǒng)型的大框架的主要賣點(diǎn)之一,大量現(xiàn)成的UI組件以及與框架本身的親和性,可以大幅提高開發(fā)效率。YUI、Dojo是典型代表。在選擇時,需要注意的一點(diǎn)是可定制性。
數(shù)據(jù)繪圖:這是一類比較特定的需求,但其實(shí)現(xiàn)的復(fù)雜程度也非常高。ExtJS封裝了強(qiáng)大的數(shù)據(jù)圖表功能,除此之外也有專門針對數(shù)據(jù)可視化的庫如D3.js。
工具鏈
隨著前端項(xiàng)目越來越大,維護(hù)和上線的流程也越來越復(fù)雜。利用好各類工具實(shí)現(xiàn)自動化,可以大幅提高效率。隨著Node.js社區(qū)的迅猛發(fā)展,各類基于JavaScript的命令行工具大量出現(xiàn),這其中就有許多針對前端開發(fā)的優(yōu)秀項(xiàng)目。
編譯工具:這里的編譯嚴(yán)格來說是指js的組合和壓縮。通常大型項(xiàng)目都會有多達(dá)幾百KB的js文件,采用模塊化開發(fā)的話,文件數(shù)量也會非常龐大,進(jìn)行壓縮打 包是必不可少的過程。大型框架如Closure Library、Dojo、YUI都自帶編譯工具。如果使用了模塊管理庫,例如RequireJS和SeaJS,也可以使用它們自帶的打包工具。其他情況 下則可以自己寫build script,也可以借助任務(wù)化的編譯工具如Grunt.js或者Jake。
包管理工具:很久以來,前端開發(fā)者都需要自己下載、管理各類第三方庫。前端的包管理機(jī)制的好處是可以更方便地管理第三庫的版本和相互之間的依賴。目前國外 比較流行的單純的前端包管理工具有Twitter的Bower,也有混合了包管理和編譯于一體的Ender.js和Volo.js,更有集組件框架、包管理和編譯于一體的Component。國內(nèi)方面,SeaJS提供包管理+編譯工具spm。
單元測試工具:前端也需要單元測試。同樣的,傳統(tǒng)的大型框架也大多自帶單元測試工具。單獨(dú)的測試框架中比較流行而且簡單易用的有QUnit、 Jasmine、Mocha等。此外,還有一些更復(fù)雜的前端測試框架,包含了在各類瀏覽器里的自動化測試,這又可以單獨(dú)開出一個話題,限于篇幅不再深入, 感興趣的朋友可以參考:http://stackoverflow.com/questions/300855/looking-for-a- better-javascript-unit-test-tool。
框架的分類
以下根據(jù)風(fēng)格對一些主流框架進(jìn)行了粗略的分類,但分類并不是絕對的,只是為了簡化比較的過程。
封裝型
典型如jQuery、MooTools,國內(nèi)則有百度的Tangram。這一類框架通常只針對上述需求列表中的“封裝原生API”這一塊。雖然有插件機(jī)制,但通常不提供任何架構(gòu)方面的幫助,因此現(xiàn)在更多的是和架構(gòu)類的輕量框架搭配使用。
傳統(tǒng)型
典型如Dojo、YUI、Closure Library、ExtJS等,國內(nèi)則有阿里的KISSY、網(wǎng)易的NEJ、騰訊的JX等。支付寶玉伯將這一類比喻為“大教堂風(fēng)格”,一般有這些特點(diǎn):穩(wěn) 定,經(jīng)受過實(shí)戰(zhàn)的考驗(yàn);覆蓋的問題全面,試圖在自身范圍內(nèi)解決盡可能多的問題;代碼風(fēng)格和質(zhì)量一致,也經(jīng)常會要求使用者遵循一定的風(fēng)格規(guī)范;文檔豐富詳 細(xì);更新穩(wěn)重、緩慢;排他性,一旦選擇很難替換;通常帶有UI組件庫。
開放型
就國內(nèi)外的典型例子來說,國外有由Node.js著名活躍開發(fā)者TJ Holowaychuck所牽頭的Component(https://github.com/component/component),國內(nèi)則有阿里的Arale和豆瓣的Oz。
開放型的框架專注于提供開放的基礎(chǔ)架構(gòu),即代碼組織方式和工具鏈。它們也提供一部分現(xiàn)成的模塊,但使用者可以靈活地書寫模塊,或是博采眾家之長,將第三方的庫整合為模塊來使用,又被稱之為“集市風(fēng)格”。
單頁應(yīng)用型
代表性的如Backbone.js、Ember.js、AngularJS、Knockout.js等。單頁應(yīng)用的優(yōu)勢是服務(wù)器請求數(shù)少、UI反應(yīng)快速、用戶體驗(yàn)流暢。對于交互復(fù)雜的大型應(yīng)用,尤其需要有一個為單頁應(yīng)用量身打造的前端架構(gòu)來支撐。
這一類框架在近兩年大量涌現(xiàn),如上文所說,存在著許多不同的觀點(diǎn)。
這些問題目前還沒法得出決定性的結(jié)論,更多的是看開發(fā)者的個人偏好和習(xí)慣更適應(yīng)哪一種。好在這類框架目前社區(qū)都相當(dāng)活躍,因此可以找到不少參考。
值得一提的是,一些老牌的傳統(tǒng)型框架如Dojo、YUI、ExtJS都開始引入了MVC單頁應(yīng)用架構(gòu)。但Dojo和YUI的這部分功能仍處在比較粗糙的階段,只有ExtJS進(jìn)行了整體重大升級,相對成熟一些。
特例
Twitter在2月初剛開源了它的前端框架Flight。這是一個介于單頁應(yīng)用型和開放型之間的框架。其核心是事件驅(qū)動的、基于DOM的組件機(jī)制。它強(qiáng)調(diào)組件之間相對獨(dú)立松散的架構(gòu),但對組件的寫法定義十分嚴(yán)格,具有一定的侵入性和排他性。
選擇框架時應(yīng)該考慮什么
項(xiàng)目規(guī)模:小項(xiàng)目需要快速迭代,需要靈活性較高、兼容性比較好的架構(gòu)。而大項(xiàng)目則需要關(guān)注成熟度、風(fēng)格規(guī)范、可協(xié)作性、可維護(hù)性和可測試性。
團(tuán)隊(duì)的現(xiàn)有資源:團(tuán)隊(duì)是否對后臺技術(shù)選擇和架構(gòu)有一定的偏好?是否已經(jīng)對某些框架/工具有實(shí)戰(zhàn)的經(jīng)驗(yàn)?如果選擇一個開放式的框架,是否有足夠的精力來整合各類第三方工具?最后,選擇一個團(tuán)隊(duì)不熟悉的框架,需要衡量帶來的好處是否能抵消掉學(xué)習(xí)成本。
產(chǎn)品對用戶體驗(yàn)的需求:產(chǎn)品本身更適合做成單頁應(yīng)用還是傳統(tǒng)Web應(yīng)用?產(chǎn)品是需要大量現(xiàn)成的UI組件,還是需要注重每一個細(xì)節(jié)?
建議在選擇框架時,首先對自己的項(xiàng)目和團(tuán)隊(duì)進(jìn)行定位,然后總結(jié)出具體的技術(shù)需求列表,最后參照上文列舉的各項(xiàng)細(xì)節(jié)來尋找適合的框架和周邊工具。當(dāng)然,本文無法窮舉所有的細(xì)節(jié),只能提供大致的選擇方向。確定幾個潛在選擇之后,還需要開發(fā)者自己進(jìn)行深入的研究和試用。
總結(jié)
前端技術(shù)正處在一個新老并存、百家爭鳴的時代。一方面我們需要等待HTML5以及其他W3C標(biāo)準(zhǔn)的普及,另一方面更新的標(biāo)準(zhǔn),如Web Components和ECMAScript 6又已在起草之中。這兩個標(biāo)準(zhǔn)普及時,前端架構(gòu)恐怕又會迎來一次洗牌。但不管怎樣,做好足夠的功課,然后根據(jù)自己的實(shí)際需求出發(fā)來進(jìn)行選擇總是沒錯的。