淺談AngularJS模板

angularjs

做爲最流行的MVVM(Model-View-View-Model)框架之一,相信大部分前端對AngularJS都不會陌生,我也同樣久仰大名。不得不說,AngularJS所帶來的改變是巨大的,被稱爲將來瀏覽器的模式一點也不爲過,尤爲是思惟上的轉變。html

做爲一個常年揮舞着jQuery去指揮無窮無盡的DOM的前端,初次接觸AngularJS是有困難的,許多先賢警告咱們不要在AngularJS中使用jQuery,不是沒有道理的。即便AngularJS中帶有jQlite對象,也僅僅是爲了彌補一些地方AngularJS的侷限性。AngularJS操做UI的方式與jQuery有着極大區別,在深刻學習以後,我漸漸的發現了這點。過去使用jQuery的前端就像一個操縱提線木偶的傀儡師,而手握AngularJS的前端簡直是徹徹底底的魔法師。前端開發者再也不須要根據數據去改變DOM,而後填入數據,咱們所要作的僅僅是決定數據的表現形式後等待數據的注入。文檔流中的元素就像活過來了同樣,根據數據表現出了對應的樣子。前端

這一切的核心除了匪夷所思的DOM監聽機制,還有就是AngularJS的模板(template)以及其中多不勝數的內置指令(directive)了。所以,我將在本文中談談AngularJS的模板以及其思惟模式。
angularjs

模板中的內置指令

AngularJS模板和EmberJS的模板相比更爲普通一些,仍然是HTML格式的文件。這使得許多人並未真正瞭解AngularJS的模板,而認爲AngularJS只是提供了一堆內置指令並可用於HTML文件。不過就先來講說這些內置指令吧,模板後面再詳細討論。數據庫

  1. ngIf是個對於模板很重要的指令,它是基本的條件表達,知足條件時則存在,不知足則不存在。經過它能夠輕鬆的讓模板基於數據呈現不一樣結構。另外它會造成獨立Scope,這也是其與ngShow/ngHide的區別之一。若是ngIf出現太多可能會致使頁面渲染速度下降,此時能夠選擇ngSwitch來代替它,不過此時最好先一下檢查你的邏輯。express

  2. ngRepeat則是另外一重要指令,能循環建立DOM。能夠說只要數據中有數組等結構,這一指令就必不可少。配合$index等索引變量,ngRepeat能夠創造出多種形式的列表。還有ngRepeatStart/ngRepeatEnd能夠將2個元素之間的內容循環建立,但我不多使用它們,由於這種混合多種元素的HTML結構不太好。segmentfault

  3. ngClass是樣式層面上的主要指令,它的值能夠是存放class名的變量,也能夠是帶有條件的對象。如此能夠經過表達式來選擇須要的class,以呈現不一樣的樣式。ngHide與ngShow其實就是特殊的ngClass,ng-hide="[expression]"至關於ng-class="{'hide': [expression]}"。後端

這些基本的指令構成了一套頗有效的模板邏輯,咱們能夠消除掉各類HTML的重複性代碼,還能在單個模板中呈現出無數的形式。但我不同意將大段的HTML作成partial或directive並經過switch或if來選擇性呈現,由於模板應該是可複用的組件,而不是帶有邏輯的路由。數組

真正的頁面只有一個

AngularJS的主旨即快速建立單頁面應用,所謂單頁面就是說真正的頁面只有一個,其中變化的只是模板和數據。但許多習慣於傳統Web的開發人員每每找不到單頁面的感受,天然而然的將模板看成了以往的HTML頁面,而後根據AngularJS的路由一一對應到模板。這是時常發生的狀況,即便是在開發的中途也可能轉入這個誤區,由於模板的邏輯難以劃分,還有被控制器牽連進去的,最後只能讓每一個路由單獨使用對應模板。瀏覽器

模板不是頁面,不該該和路由有任何邏輯關係,它關心的應該僅僅是數據呈現結構。所謂的數據呈現結構是說數據須要以何種方式呈現,好比列表、統計圖、詳情或分頁等,而不是數據結構。它每每依賴於HTML結構,因此當HTML的結構不夠表意時,模板的劃分也會跟着變得困難。一個模板就是一個對象,不屬於它的東西,不管多麻煩,即便HTML能夠放在一塊兒,也必定要排除在外,不管封裝成directive仍是partial,或者分離出去與之變成平級關係。數據結構

模板也不該該關心數據的內容,即便兩個路由中顯示的數據內容徹底不同,但顯示結構同樣或相似,模板就應該利用自身的邏輯在注入數據後呈現對應的內容。但值得注意的是,其自身邏輯應該判斷數據自己而不是引用控制器的邏輯,也就是說模板要有自身的邏輯,不該該將頁面邏輯混合散落在模板和控制器中。那麼如何作纔好呢,簡而言之就是UI邏輯和業務邏輯的分離,模板中應該只存在UI邏輯。咱們應該封裝模板,像黑盒同樣,不管控制器有什麼業務邏輯,都不該該干涉UI邏輯。

好比ngIf指令可使用表達式,咱們應該利用數據自己達到邏輯表達,而不是依賴於控制器中依賴於業務邏輯的變量,好比使用data.length > 0而不是hasData。這看起來是同樣的,但其實區別很大,前者將具體邏輯放到了模板中,當數據爲空時不顯示,然後者則把邏輯拋給了控制器。這樣就不只僅是邏輯放置在哪的問題了,這是違背MVVM框架初衷的。由於當data爲空後,控制器必須以前在監聽data,並將hasData設爲false,而這些事應該交由AngularJS在數據綁定時自動完成。因此若是實在分不清什麼邏輯是UI邏輯要放在模板中,什麼邏輯是業務邏輯該放在控制器中,那就遵循一個原則,能利用Angular性能時就利用,不要作AngularJS已經作過的事。

Scope屬於誰?

其實這是思惟方式的轉變,因此誤入歧途是不可避免的,而其中最多的誤解在於Scope。Scope自己是做爲在控制器中的模板做用域,實際效果就是Scope上的屬性在模板中能夠直接使用,不管是在花括號仍是Angular的表達式中,好比指令中。但開發人員很容易被其表面所迷惑,將其看成傳統後端模板的變量來使用,更有甚者發明了$scope.vm做爲ViewModule來使用,但其實Scope自己就應該是ViewModule。其緣由仍是在於思惟,不一樣於傳統後端的模板變量,Scope是依賴注入到控制器的,也就是說Scope不屬於控制器,而屬於模板。很明顯在模板中將不須要Scope這一名稱來指明,由於全部變量都是Scope的。

Scope屬於控制器或模板有區別嗎?有並且很是大。當Scope一片空白的來到控制器時,咱們天然而然的認定這樣一個原先空空的變量確定是被控制器創造出的,而後在模板中被使用。但咱們換個角度想一想,若是模板先定義了它須要的Scope的結構,然後控制器僅僅只是按照預先的定義插入對應的數據,Scope的結構是否是明顯屬於模板。仔細想一想其實很好理解,模板和任何控制器組合都是這套Scope結構,而控制器脫離了該模板Scope就變了,並且模板不用的東西,控制器放到Scope中也等於沒有。也就是說,每每咱們先定義了控制器纔開始編輯模板,天然認爲Scope是控制器建立好給模板用的,但若是咱們先建立模板,控制器其實只能按照模板中規定的結構來填入數據。而這就像是Java中的接口同樣,模板定義好接口,而後控制器只要知足這些填入本身的數據,就能在頁面上得到須要的東西,並且一樣它們都是一對多的關係。也許有人會說,那也存在一樣的邏輯和結構卻須要不一樣顯示方式的狀況。其實,這僅僅是表現形式的改變,數據呈現結構卻沒有發生本質上的改變,一個列表,不管是列、行仍是格子都仍然是一個表格,模板應該用ngRepeat將其呈現,而後就是CSS的事情了。當你遇到沒法使用CSS轉變呈現方式的時候,首當其衝的應該考慮一下,是否HTML寫的很差,不夠靈活,沒有語義化呢。

當咱們完成這樣的思惟轉變,模板將再也不依賴於控制器,它也能夠完整的自我封裝起來。這樣作的好處顯而易見,HTML的代碼將有巨大的精簡,重複的代碼消失的無影無蹤,由於當它們重複時就能夠造成一個獨立的模板,模板能夠套模板,它們還能夠互相引用。但要注意循環引用,好比a引用了b而b又引用了a。如此CSS的重構也將變得更加簡單,此時考慮使用OOCSS等提升CSS複用性將變得易如反掌。另外若是某個模板具備關聯十分緊密的複雜邏輯還能夠打包作成指令,這樣模板的邏輯將更加的強大。

命名轉變思惟

要作到這樣的思惟轉變實際上是很難的,不過有一個小技巧能夠藉以完成這樣的轉變,那就是命名。當設計模板Scope結構時,咱們能夠更多的考慮這樣的一個結構中某數據是什麼。在控制器中,也許是一組系統管理員的數據,咱們能夠命名爲admins,也許是一組用戶數據users,又或者是一組讀者數據reader,還多是採購員buyer。不過等等,一旦使用了這之中的某個命名,模板就會和控制器綁在一塊兒,你沒法爲其餘的數據使用該模板,而偏偏對於設計統一性來講,這些數據又極可能會放在同種結構之中,也許僅僅是顏色或標題的差別。

此時還能作什麼,複製一份相同的模板而後選擇另外的命名?徹底錯誤,咱們應該抽象它們,它們都是一類數據,列表list。就像以前我說的,設計模板時僅僅應該關心其數據表現結構,它就是一個列表,列表的數據內容徹底不會影響到結構。不管是users仍是buyer,均可以放入一個list模板中。這是很是重要的,一個長期和數據庫打交道的後端,慣性思惟會將user和users歸爲一類,並認爲admins和users是徹底不一樣的。可是到了UI層面,這些顯而易見的規則已經不適用了,users和admins都是list,而admin和user都是object。若是你擔憂不一樣數據類型須要的UI表現不同,那就應該先考慮CSS實現兼容各類形式,其次是考慮嵌入一些不一樣的模板或指令來實現差別化,而不是直接複製兩個模板。

總之,使用AngularJS必定不要過多考慮流程,而要考慮UI自己,我以爲這有點像面向對象的思惟方式,UI既對象。利用AngularJS的監聽機制讓數據驅動模板產生交互。甚至我還常常利用JavaScript中Object的引用類型特性,使一些關聯的數據之間也能產生數據與視圖之間的關聯,這樣連通UI與數據和數據與數據以後就不用再管UI了,只須要操做數據便可,UI天然會表現出應有的狀態。

博客原文地址

相關文章
相關標籤/搜索