YY在使用AngularJS開發(fā)云控制臺前端框架的總結(jié)
為什么選擇AngularJS
輕松構(gòu)建單頁面應(yīng)用
可以說,這是我們最終選擇AngularJS的重要原因,如果你希望構(gòu)建一個結(jié)構(gòu)清晰、可維護、開發(fā)效率高、體驗好的單頁應(yīng)用,AngularJS是相當(dāng)不錯的框架。
單頁面應(yīng)用的魅力
什么是單頁面應(yīng)用?單頁應(yīng)用,指一種基于Web的應(yīng)用或者網(wǎng)站,頁面永遠都是局部更新元素,而不是整頁刷新,給用戶的感覺就像整個網(wǎng)站都是一個頁面。當(dāng)用戶點擊某個菜單或者按鍵時,不會跳轉(zhuǎn)到其他頁面,前端會從后端獲取對應(yīng)頁面的數(shù)據(jù)而不是HTML,之后在頁面中需要更新內(nèi)容的地方,局部動態(tài)刷新,而如果是傳統(tǒng)的多頁網(wǎng)站,當(dāng)用戶訪問不同的頁面時,服務(wù)器會直接返回一個HTML,然后瀏覽器負責(zé)將這個HTML展現(xiàn)給用戶。目前,大部分云控制臺,都是單頁應(yīng)用架構(gòu),單頁應(yīng)用能帶來一種更近似客戶端,而不是網(wǎng)頁的體驗。單頁面應(yīng)用網(wǎng)站,在體驗方面,具有如下優(yōu)點:
做“頁面跳轉(zhuǎn)”時,永遠都是局部動態(tài)刷新,用戶不會感覺整個屏幕閃了一下,而僅僅是需要變化的區(qū)域做了局部刷新。例如兩個不同的頁面,假設(shè)頁面元素都是一樣的,只是元素中的文字不一樣(例如每個頁面都有一個面包屑,一排按鈕及一個表格,這幾個元素的布局也是一樣的),當(dāng)用戶跳轉(zhuǎn)到另外一個頁面時,會看到整個頁面并沒有重新渲染,只是文字發(fā)生了變化。簡單地說,這有點類似使用App,永遠都是局部發(fā)生變化,你見過哪個App,當(dāng)點擊到不同的功能視圖時,整個屏幕會白屏閃一下的么?
URL可收藏,可回退。如果瀏覽器的URL一直不變,那還不能稱為真正的單頁應(yīng)用。不同的功能,不同的資源頁面,對應(yīng)的URL是不一樣的。當(dāng)用戶跳轉(zhuǎn)到另外的功能時,會發(fā)現(xiàn)URL會變成對應(yīng)的值;通過回退鍵,也可以回到之前瀏覽的頁面。這個特性有什么用呢?如果URL不會隨著功能而變化,當(dāng)用戶刷新當(dāng)前頁面時,就會回到之前的默認頁面,而不是預(yù)期的當(dāng)前功能頁面,同樣的,URL也不具備可收藏性,因為點開之前收藏的URL,永遠都是網(wǎng)站的默認頁面。這對一些強交互的管控系統(tǒng)來說,體驗不好。
功能切換時快速流暢??焖倭鲿持饕驗閮蓚€原因:一是頁面都是局部刷新,從用戶感官來說更快;二是和后臺通信的內(nèi)容,都是數(shù)據(jù),而不是頁面模板,請求量更少;而傳統(tǒng)網(wǎng)站,在訪問不同頁面時,服務(wù)端返回的是HTML,體積更大,而且還需要一直重復(fù)加載Java、CSS文件。
因為網(wǎng)站的單頁化,可以更好地使用全局類的交互(做頁面切換時,需要一直保持不變的交互)。例如,頁面上需要顯示某次耗時操作的進度,例如上傳文件進度,耗時操作的當(dāng)前狀態(tài)等,你可以在頁面最右側(cè)固定顯示進度。當(dāng)用戶訪問單頁應(yīng)用時,他會明白,當(dāng)點擊其他模塊時,這個右側(cè)的通知欄不會消失,會固定顯示。而如果是多頁網(wǎng)站,用戶則會困惑,擔(dān)心自己跳轉(zhuǎn)到其他頁面后,進度通知就會消失。
AngularJS是開發(fā)單頁應(yīng)用的利器
單頁面應(yīng)用對于代碼分層,結(jié)構(gòu)清晰有更高的要求,而AngularJS是一個MVVM框架,其自身的約定,減少了我們寫出“一鍋粥”代碼的可能性(在下面討論“編寫更易維護的代碼”時會詳述)。
AngularJS的著名第三方組件UI-Router,是一個控制頁面路由的組件,它支持我們快速搭建單頁應(yīng)用(AngularJS本身的路由功能也可以,但功能會稍微弱一些)。
AngularJS的檢查更新機制,使頁面刷新時更加快速自然。 當(dāng)數(shù)據(jù)“可能”發(fā)生變化時,AngularJS會檢查出所有變化的元素,再“一次性”刷新所有變化的UI元素。
編寫更易維護的代碼
很多人經(jīng)常會抱怨,不同水平的人湊在一起寫Java,到最后項目經(jīng)常就是一鍋粥,同一個Java文件里面,各種各樣的邏輯都混在一起,要增刪功能,簡直是惡夢。無規(guī)矩不成方圓。作為框架,AngularJS無疑能大大改善這種狀況,使得項目整體的分層明了,職責(zé)清晰。在筆者看來,AngularJS能夠幫助我們編寫更易維護的代碼,一是因為其“關(guān)注點分離”的理念,二是因為其強大的特性為項目節(jié)省了不少代碼量,從而降低程序員犯錯的概率。
關(guān)注點分離
關(guān)注點分離是AngularJS的一大設(shè)計哲學(xué)。所謂關(guān)注點分離,指的是各個邏輯層職責(zé)清晰明確。例如,當(dāng)你需要修改甚至替換展現(xiàn)層時,無需關(guān)注業(yè)務(wù)層是怎么實現(xiàn)的。在AngularJS中,服務(wù)層(Ajax請求)- 業(yè)務(wù)層(Controller)- 展現(xiàn)層(HTML 模板)- 交互層(animation)這些都有對應(yīng)的基礎(chǔ)組件。不同組件職責(zé)不同,也很難將本屬于B組件的職責(zé)放到A組件上去實現(xiàn)。舉幾個例子:
HTML及Controller需要協(xié)同工作,但職責(zé)分明。視圖、交互層面的邏輯,例如展示隱藏某些元素,是HTML模板的職責(zé),Controller只能用于數(shù)據(jù)初始化。如果你反其道而行,想在Controller中做HTML模板做的事情,將會非常別扭。這一點非常重要,傳統(tǒng)的Java代碼,經(jīng)常會出現(xiàn)的情況,就是Java里面會有大量DOM操作的邏輯,同時還有大量數(shù)據(jù)操作相關(guān)的邏輯,這些邏輯耦合到一起,當(dāng)需要單獨重構(gòu)數(shù)據(jù)層或者視圖層時(例如項目UI改版),都會捉襟見肘,同時,由于Java代碼量的迅速膨脹,維護起來也會很麻煩。
你無法將后臺通信邏輯放到Controller中實現(xiàn),而要放到Factory中。后臺通信邏輯,一般要做成公用的。而由于Controller之間是不能相互調(diào)用的,所以你也不可能將后臺通信邏輯放到其中一個Controller,然后其他Controller來調(diào)用這個Controller暴露的接口。唯一的辦法,就是將后臺通信邏輯放到Factory或者Service中。
Filter及Directive看似都可以用于數(shù)據(jù)轉(zhuǎn)換,但實則不同。由于Filter只能做數(shù)據(jù)格式化,不支持引入模板,所以公用的UI交互,涉及到DOM元素或者需要引入HTML模板時時,也只能通過Directive來實現(xiàn)。
綜上所述,AngularJS項目,其展現(xiàn)層、交互層的邏輯,都是在HTML或者指令中,服務(wù)層(后臺通信),只適合出現(xiàn)在Factory(或者Service)中,而業(yè)務(wù)層則由Controller來負責(zé)。這樣每層的邏輯都是輕薄的,而不是糾結(jié)在一起。 如果你只是要優(yōu)化展示邏輯,那改改HTML就可以了,不用去管Controller是怎么寫的。這一點我們有親身體會。項目開發(fā)過程中,我們重構(gòu)了視覺效果,所有的HTML都要重寫。但在重構(gòu)時,我們的Controller、后臺通信(Service)、Filter基本都不用改,只要改HTML就行了。而如果項目是用jQuery寫的,顯然不可能做到,你需要重新為新的HTML增加一些可供jQuery選擇器使用的class或id,然后需要在Java里面綁定事件,根據(jù)新的CSS樣式名來寫新的交互效果,而在AngularJS上,有些不用做了(例如為了jQuery選擇器,而為HTML元素增加新的class、id;在Java中綁定事件),有些(例如交互效果)則只要改HTML就行,而不是改Java。
AngularJS為我們省去的代碼量
對于任何項目來說,代碼臃腫、冗余是可維護性的天敵。因此,實現(xiàn)同樣的功能,代碼量越少,抽象度越高,冗余度越低,在某種程度上意味著項目更方便維護。而能減少代碼量,也是AngularJS被推崇的一大優(yōu)點。讓我們來看看,它是如何減少了代碼量的:
首先,作為一個大而全的框架(雙刃劍,有利有弊),AngularJS提供的諸多特性,使我們可以更專注于業(yè)務(wù)代碼的編寫。
其次,AngularJS雙向數(shù)據(jù)綁定的特性,將我們從大量的值綁定代碼中解放出來。和jQuery對比,AngularJS不用為了選擇某個元素,而刻意為HTML加上一些跟樣式無關(guān)的class、2-2-2. id;不用寫一堆從HTML元素中取值,設(shè)值的代碼;不用在Java代碼中綁定事件;不用在Java值發(fā)生變化時寫代碼去更新HTML對應(yīng)的值。雙向數(shù)據(jù)綁定,讓我們告別很多簡單無趣的綁定事件、綁定值的代碼。
Directive、Filter、Factory等,天然的就是一個個可以復(fù)用的組件,減少了冗余重復(fù)代碼。一些需要公用的邏輯,如果放在Controller中,都會相當(dāng)別扭,就這樣被AngularJS“逼著”,把公用邏輯都放到Directive、Filter、Factory中去。
開發(fā)心得總結(jié)
我理解的AngularJS基礎(chǔ)組件
化繁為簡,幾大基礎(chǔ)組件的使用場景
首先我們需要理清AngularJS幾個組件的使用場景。AngularJS的一個毛病,就是新概念,新特性太多,新手一下子要了解這么多,學(xué)習(xí)曲線略陡。為了幫助大家理解,總結(jié)下我理解的幾大組件使用場景。
請求資源與數(shù)據(jù)緩存的東西放進Service。Factory、Service本質(zhì)上都是Provider的語法糖,兩者只是使用方式有所不同,建議大家直接都用Service,ES6 class更容易,之后想平滑遷移到Angular2也會更容易,同時也能避免團隊成員在選擇Service還是Factory時產(chǎn)生困惑。
數(shù)據(jù)需要格式化的東西用Filter處理。例如把status值轉(zhuǎn)化為中文值,把時間戳轉(zhuǎn)成時間字符串之類。
需要公用的DOM操作,放在指令中去寫。另外,如果需要引入jQuery組件,也可以寫個指令把jQuery組件初始化代碼放進去。
Controller與視圖按照一對一的關(guān)系維護,在Controller內(nèi)初始化Scope對象與在Scope上添加方法(行為),為ViewModel做賦值。其他所有過程都不應(yīng)該出現(xiàn)在Controller中。Controller中不應(yīng)該出現(xiàn)和頁面展示、交互相關(guān)的代碼。例如展示隱藏某些元素之類,這些應(yīng)該是HTML模板或者指令負責(zé)。代碼越薄越好。
全局常量值放到Constant。
指令(Directive)魔法
指令這個特性,用“魔法”一詞來形容它,都不為過。
解決的痛點:一言以蔽之,指令提供了一套前端組件化的方法及約定,這使得編寫,使用UI組件更加方便了。相對于jQuery,它解決了以下痛點:
動態(tài)生成了HTML元素后,不用再手動去為其加上Java特性。舉個例子:HTML原生的checkbox框比較丑,在jQuery時代,可以將checkbox替換成自定義的效果,如果是頁面一開始就有的checkbox,我們可以在document.ready的時候調(diào)用自定義checkbox的初始化方法。但是,如果這個checkbox是動態(tài)生成的,在每個動態(tài)生成checkbox的地方,我們都得去調(diào)用checkbox的初始化方法,相當(dāng)麻煩。但用了AngularJS的指令,就不會有這個問題了,只要在模板的chceckbox中加上指令,不管這個模板是動態(tài)變化的還是靜態(tài)的,無需通過業(yè)務(wù)代碼來逐個調(diào)用初始化方法,呈現(xiàn)給用戶的,就已經(jīng)是AngularJS替換后的checkbox效果。
一個組件的HTML和Java,是一個整體,而不是割裂的(題外話,這一點React做得比Angular1.x還要好)?;趈Query的UI組件,其引入方法,經(jīng)常是這樣的,首先,要求你自己copy一段指定的HTML,然后再調(diào)用初始化方法。而指令則支持定義對應(yīng)的模板HTML,用戶在引入時,可能只要寫一個指令標簽,就會自動生成N行的HTML及綁定對應(yīng)的Java效果。當(dāng)然,理論上jQuery也能做到這樣,但是會比Angular的實現(xiàn)麻煩許多。
應(yīng)用、移除UI特性時方便直觀。假設(shè)有這么一個需求,給一個普通輸入框增加輸入限制,只能輸入特定字符(如字母數(shù)字),寫好對應(yīng)指令,只要給這個input輸入框加上這個指令標簽,就能馬上應(yīng)用這個特性,之后要移除,只要把標簽去掉就好。相比之下,jQuery就會麻煩多。jQuery下,一般是通過元素選擇器來綁定Java效果。因此,在添加該特性時,你需要考慮給對應(yīng)的輸入框指定一個合適的元素選擇器。移除特性時,你要考慮:有可能你在動態(tài)生成輸入框的地方,都加了這些初始化代碼,這些Java都需要移除;如果元素選擇器用的是class,得考慮是不是其他輸入框也有這個class,如果是,那么移除代碼時也會影響到其他輸入框。
技巧
如果你迫不得已需要引入jQuery組件,你可以寫一個指令把它包裝起來,在該指令中初始化組件。
要注意require參數(shù)中的值是駝峰的,在HTML中就得轉(zhuǎn)成對應(yīng)的中劃線命名,例如有require參數(shù)phoneKey,那么HTML中應(yīng)為phone-key=”xxx”。雖然這個道理很淺顯,但經(jīng)常一不小心就會弄錯了,然后發(fā)現(xiàn)在指令內(nèi)部怎么著都拿不到require參數(shù)。
如果你在link中加了elm.bind(‘click’),當(dāng)click回調(diào)函數(shù)中,作用域的值發(fā)生變化,記得調(diào)用scope.$apply(),否則值變化不會生效。
文件、目錄約定
目錄結(jié)構(gòu)
第三方庫、CSS、圖片放置到哪個目錄,不在本文討論范圍,這里略過。需要進一步說明的,是業(yè)務(wù)代碼目錄。

我們將系統(tǒng)自身的Java、HTML模板都放在pages目錄,其中子目錄common放置公用的Java及模板;其他子目錄,以功能模塊名作為目錄名,然后將這個模塊相關(guān)的Java及模板放在其中。這樣開發(fā)同個模塊功能時,可以方便地在HTML及對應(yīng)Java之間切換。有些代碼規(guī)范可能還會建議在模塊這一級目錄下,再根據(jù)AngularJS的幾大組件Controller、Filter、Service等,創(chuàng)建不同的子目錄,例如模塊A/controller,模塊A/service之類,我們則將所有Java及HTML放在同一級,這樣做主要有幾個原因:
我們的項目,平均每個模塊只有十來個文件,特別是每個模塊一般只有一個Filter及Service,為了這一個文件創(chuàng)建一個目錄,顯得多此一舉。
通過文件名,已經(jīng)可以很方便地區(qū)分不同的Java組件類型及HTML模板類型,同時,由于IDE一般會按照文件名字母排序,所以相同功能的Java及HTML會挨在一起,查找對應(yīng)的模板或Java代碼會方便很多。
非常好我支持^.^
(0) 0%
不好我反對
(0) 0%
