擺脫DOM操作,從TodoMVC看angularJS

發表於2015-10-08

取代jQuery?

我很久之前便聽說了angularJS的大名,之前的leader也經常感嘆angularJS的設計如何如何精妙,可嘆一直沒有機會深入瞭解,國慶長假因為沒錢出遊,倒是可以對他做一個瞭解……

根據之前的經驗,就現有的前端專案,如果最初沒有良好的設計,做到一定階段一定會變得難以維護,就算最初有設計,變化無常的PM也會讓你的專案BUG叢生。

一個頁面的複雜程度不斷的增加,依賴模組也會變得混亂,而其中最為頭疼的就是頁面級隨心所欲的DOM操作了!

MVC類的框架可以很好的解決以上問題,而號稱MVVM的angularJS在處理這種情況似乎更有話語權,所以我們今天便來好好研究其一番。

angular適合做具有複雜資料互動的前端應用,他旨在讓我們擺脫繁瑣的DOM操作,而將注意力集中在業務邏輯上,這裡擺脫繁瑣的DOM操作是個非常關鍵的願景,也是很多人不太理解,甚至會將jQuery這種庫與Backbone或者angularJS這種框架做對比的原因。

這裡舉個jQuery不依賴MVC骨架的例子,我們的訂單填寫頁,需要在商品數量變化後導致金額變化,並且沒有選商品時,支付按鈕不可點選:

對於一個有些經驗的菜鳥來說,可能會這樣寫程式碼:

對於一些有一定經驗的老鳥來說,可能會這樣寫程式碼:

第一段程式碼可能會導致你年底加薪無望,並且在團隊中沒有話語權;而第二段程式碼積累到一定量後會讓這個專案變得不可維護:

① 支付工具欄初始化狀態如何顯示,如果數字元件按需做非同步載入,這個顯示將變得更加負責。

② 哪些操作將導致支付欄變化,你如何組織這些變化的程式碼,是讓他四散到各處,還是集合在一起,集合後導致函式過大怎麼辦?

③ 新增的導致工具欄變化的操作會不會對原來的操作造成影響,新增的程式碼放在何處?

④ 如果有地方要使用工具欄處的資訊,取的資訊會不會是無效的(取的時候可能正在變化),應該通過DOM取還是記憶體取?

⑤ 如果支付欄DOM結構如果變化,對你的程式影響有多大,如何主流程的影響,比較支付點選後只需要運算元據,不需要關注DOM?

⑥ ……

這個就是僅僅依賴jQuery要面臨的問題,並且這種問題是無解的,因為這裡的專注點是DOM操作而不是資料,如果將關注點變成了資料,程式碼就不是這樣寫的,DOM操作僅僅是過程而不是目的,我們程式碼的目的,往往是展示資料、獲取資料,這點一定要清晰。

所以讓我們帶著這些問題:angular的優勢在何處,他如何改善我們的程式設計體驗,進入今天的學習吧。

初探angularJS

Hello World

學習任何一門語言,Hello world是必不可少的,他是我們邁向精通的唯一路徑:

被{{}}包裹的便是angularJS變數,上述程式稍作改變的話:

便會同步顯示文字框輸入內容,這裡通訊的基礎是model對應著ng-model,只要被ng-app包裹就會受angularJS控制,用angularJS自己的話說:HTML標籤增強

作用域

為什麼文字框中的變化會體現在外層,這個涉及到了ng-model的雙向繫結知識,我們暫時不予理睬,但是外層又是從哪裡讀取name這個變數的呢?

在angular中,屬性會儲存在一個@scope(作用域)的物件上,每次我們對文字框的更新皆會通知scope上的name屬性,在angular中,scope是連線controllers(控制器)與template(檢視)的主要膠合器。

上述程式碼完全不涉及js程式碼,真實的場景中每個程式碼段會對controller做依賴,我們這裡對程式碼做一些更改:

這裡首先定義了一個application模組,後續會看見,我們每次程式碼一定會新建一個application,相當於名稱空間的意思,後面還可以做依賴用。

接著,我們建立了一個controller模組,這裡已經有點MVC的味道了,controller接受$scope屬性,這個時候模板上所有子標籤對這個控制器中的屬性便有了訪問許可權,這裡用到了一些angular指令

ng-app:告訴html標籤已經處於angular的控制了,可以使用angular的特性
ng-controller:一個module下面可以包括多個控制器,每一個標籤所屬的控制器由該指令指定

上述程式碼是將控制器中的資料讀出來,我們同樣也可以將View中的資料讀入到控制器:

PS:看到這裡,老夫虎軀為之一振,對該特性的實現產生了興趣,後續值得深入

指令

指令讓我們有能力使用angular規定的方式為HTML標籤增加新特性,angular內建了很多有用的指令,這裡仍然舉一個簡單的例子說明問題:

我們除了使用angular的內建指令外,還可以自定義指令,比如這裡的讓文字框自動獲取焦點的指令:

指令的使用可以很複雜,後續我們會更加深入,這裡再舉一個單獨使用的例子:

指令的定義有很多引數,可以指定該指令作為屬性還是作為標籤,這個我們後續再深入瞭解。

過濾器

感覺過濾器是參考的smarty的語法,一般而言是用作顯示的增強,angular本身也提供了很多內建過濾器,比如:

感覺比較有用的是日期操作過濾器:

數字格式化:

當然,我們可以使用自定義過濾器,比如這裡我想對超出某一區間的數字加…

具備了以上知識,我們嘗試進入To都MVC看看

參考:http://www.cnblogs.com/whitewolf/p/angularjs-start.html

TodoMVC

我們由最新的TodoMVC下載程式碼:http://todomvc.com/,首先檢視js引用情況:

除了angular本體檔案外,還多了個angular的擴充套件,做單頁應用的路由功能的,這個路由程式碼量不大,使用和Backbone的路由比較類似;app.js為入口檔案,配置路由的地方;餘下是控制器檔案檔案以及一個localstorage的操作服務,餘下就是指令了。

程式碼首先定義了一個模組作為本次程式的名稱空間:

ngRoute為其依賴項,可以從route的定義看出:

這裡來看看其router的配置,以及index.html的寫法:

這個程式碼現在基本看不懂,大概意思應該就是根據路由執行config中的邏輯,將模板展示在頁面上,其中index.html有一段程式碼應該是用於替換模板的:

我們先拋開那段看不懂的,直奔主流程,目光聚焦到控制器controller:

這段程式碼130行不到,讓我體會到了深深的神奇,首先我們在app中返回了讀取到localstorage的物件:

然後就在controller的依賴項中讀到了被注入的物件:

此時,模板也被插到了頁面上,等待controller的執行:

首先這裡有一個$watch方法,監控著todos的變化,每次變化都會體現到這裡,導致view的變化:

然後我們將關注點放在新增專案上:

View上的呼叫點是:

首先這段程式碼中有一個autofocus的指令,沒有什麼卵用:

可以看到model直接繫結到了該文字框上,所以addTodo方法可以直接根據$scope獲取文字框的屬性,完了呼叫單例store提供的靜態方法儲存資料,saving引數可以暫時將文字框變成不可編輯狀態,而後todo資料更新,會自動引發View變化,於是流程結束!!!

我們如果將$scope放到全域性上對其資料造成變化:

每次返回操作檢視時候,該變化會馬上反應到View上,於是我發現了以下不同:

① 因為所有與業務相關的資料全部做了雙向繫結,我根本沒有必要由dom獲取資料了,我自然而然的到$scope中獲取資料,不知道為什麼,這個特性讓我有點愉悅!

② 我要做的事情其實就是約定好資料物件,然後將該物件放到要用到的所有檢視上即可,每次記憶體中資料變化Dom會同步更新

於是通過以上兩點,我似乎得到了一個驚人的結論:

因為,前端要做的事情只不過是正確的展示伺服器端的資料,每次DOM事件造成的改變也往往是資料引起的,如果我們能做到資料變化自動更新到DOM變化的話,那麼DOM操作的必要似乎沒有了,而angular乾的事情正是如此!!!

思考

到此為止,TodoMVC的程式碼我雖然沒有完全看懂,但是他帶給我的震撼是全方位的,之前使用MVC類框架可以規範資料到DOM的操作,很大程度上解除DOM和JavaScript的耦合關係,而angular似乎完全拋開了業務資料導致的DOM變化操作!!!

我們現在團隊有一mis後臺系統,我在考慮是否要把它接過來,使用angular+bootstrap重構,可能別有一番風味吧!

最後,今天初步調研了一下angularJS,就已經感受到他的魅力了,後面時間需要將之用於實踐,並且對其設計思想作深入研究!!!

 

相關文章