AngularJS 中的一些坑

蔡蔡發表於2013-12-07

最近幾個月頻繁的跟AngularJS打交道,對於web應用開發來說Angular真的是一個神奇的框架,但是沒有東西是完美的,在這篇文章裡我會把我的感悟羅列出來,希望可以產生共鳴(前提是你對Angular已經有所瞭解)。

UI的閃爍

Angular的自動資料繫結功能是亮點,然而,他的另一面是:在Angular初始化之前,頁面中可能會給使用者呈現出沒有解析的表示式。當DOM準備就緒,Angular計算並替換相應的值。這樣就會導致出現一個醜陋的閃爍效果。

上述情形就是在Angular教程中渲染示例程式碼的樣子:

如果你做的是SPA(Single Page Application),這個問題只會在第一次載入頁面的時候出現,幸運的是,可以很容易杜絕這種情形發生: 放棄{{ }}表示式,改用ng-bind指令

 

你需要一個tag來包含這個指令,所以我新增了一個<span>給phone name.

那麼初始化的時候會發生什麼呢,這個tag裡的值會顯示(但是你可以選擇設定空值).然後,當Angular初始化並用表示式結果替換tag內部值,注意你不需要在ng-bind內部新增大括號。更簡潔了!如果你需要符合表示式,那就用ng-bind-template吧,

如果用這個指令,為了區分字串字面量和表示式,你需要使用大括號

另外一種方法就是完全隱藏元素,甚至可以隱藏整個應用,直到Angular就緒

Angular為此還提供了ng-cloak指令,工作原理就是在初始化階段inject了css規則,或者你可以包含這個css 隱藏規則到你自己的stylesheet。Angular就緒後就會移除這個cloak樣式,讓我們的應用(或者元素)立刻渲染。

Angular並不依賴jQuery。事實上,Angular原始碼裡包含了一個內嵌的輕量級的jquery:jqLite. 當Angular檢測到你的頁面裡有jQuery出現,他就會用這個jQuery而不再用jqLite,直接證據就是Angular裡的元素抽象層。比如,在directive中訪問你要應用到的元素。

(演示程式碼: this plunkr )

但是這個元素jqLite還是jQuery元素呢?取決於,手冊上這麼寫的:

Angular中所有的元素引用都會被jQuery或者jqLite包裝;他們永遠不是純DOM引用 

所以Angular如果沒有檢測到jQuery,那麼就會使用jqLite元素,hide()方法值能用於jQuery元素,所以說這個示例程式碼只能當檢測到jQuery時才可以使用。如果你(不小心)修改了AngularJS和jQuery的出現順序,這個程式碼就會失效!雖說沒事挪指令碼的順序的事情不經常發生,但是在我開始模組化程式碼的時候確實給我造成了困擾。尤其是當你開始使用模組載入器(比如 RequireJS), 我的解決辦法是在配置裡顯示的宣告Angular確實依賴jQuery

另外一種方法就是你不要通過Angular元素的包裝來呼叫jQuery特定的方法,而是使用$(element).hide(4000)來表明自己的意圖。這樣依賴,即使修改了script載入順序也沒事。

 

壓縮

特別需要注意的是Angular應用壓縮問題。否則錯誤資訊比如 ‘Unknown provider:aProvider  <- a’ 會讓你摸不到頭腦。跟其他很多東西一樣,這個錯誤在官方文件裡也是無從查起的。簡而言之,Angular依賴引數名來進行依賴注入。壓縮器壓根意識不到這個這跟Angular裡普通的引數名有啥不同,儘可能的把指令碼變短是他們職責。咋辦?用“友好壓縮法”來進行方法注入。看這裡:

to this:

 

這個陣列語法很好的解決了這個問題。我的建議是從現在開始照這個方法寫,如果你決定壓縮JavaScript,這個方法可以讓你少走很多彎路。好像是一個automatic rewriter機制,我也不太清楚這裡面是怎麼工作的。

最終一點建議:如果你想用陣列語法複寫你的functions,在所有Angular依賴注入的地方應用之。包括directives,還有directive裡的controllers。別忘了逗號(經驗之談)

注意:link function不需要陣列語法,因為他並沒有真正的注入。這是被Angular直接呼叫的函式。Directive級別的依賴注入在link function裡也是使用的。

 

 Directive永遠不會‘完成’

在directive中,一個令人掉頭髮的事就是directive已經‘完成’但你永遠不會知道。當把jQuery外掛整合到directive裡時,這個通知尤為重要。假設你想用ng-repeat把動態資料以jQuery datatable的形式顯示出來。當所有的資料在頁面中載入完成後,你只需要呼叫$(‘.mytable).dataTable()就可以了。 但是,臣妾做不到啊!

為啥呢?Angular的資料繫結是通過持續的digest迴圈實現的。基於此,Angular框架里根本沒有一個時間是‘休息’的。 一個解決方法就是將jQuery dataTable的呼叫放在當前digest迴圈外,用timeout方法就可以做到。

(例項程式碼 this plunkr )

在我們的程式碼裡甚至遇到過需要雙重巢狀$timeout。還有更瘋狂的就是新增<script>tag 到模板中,這個指令碼里回撥Angular的scope.$apply()方法。我只想說,這很不完美。基於Angular的實現機理,這很難改變。

儘管說了這麼多,Angular仍然是我最愛客戶端JS框架。你用Angular的時候遇到過其他的坑嗎?你用什麼方法解決這些問題的呢?請留言!

相關文章