本文引自:http://www.javashuo.com/article/p-uduvhkdb-cd.htmlhtml
這篇是對angularJS的一些疑點回顧,是對目前angularJS開發的各類常見問題的整理彙總。若是對文中的題目所有了然於胸,以爲對整個angular框架應該掌握的七七八八了。但願志同道合的通知補充內容前端
髒檢查機制。闡釋髒檢查機制,必須先了解以下問題。git
ng-bind 單向數據綁定($scope -> view),用於數據顯示,簡寫形式是 {{}}。angularjs
二者的區別在於頁面沒有加載完畢 {{val}} 會直接顯示到頁面,直到 Angular 渲染該綁定數據(這種行爲有可能將 {{val}} 讓用戶看到);而 ng-bind 則是在 Angular 渲染完畢後將數據顯示。github
ng-model 是雙向數據綁定(scope−>viewandview−>scope−>viewandview−>scope),用於綁定值會變化的表單元素等。web
雙向數據綁定是 AngularJS 的核心機制之一。當 view 中有任何數據變化時,會更新到 model ,當 model 中數據有變化時,view 也會同步更新,顯然,這須要一個監控。bootstrap
Angular 在 scope 模型上設置了一個 監聽隊列,用來監聽數據變化並更新 view 。segmentfault
每次綁定一個東西到 view 上時 AngularJS 就會往 watch隊列裏插入一條watch隊列裏插入一條watch,用來檢測它監視的 model 裏是否有變化的東西。 數組
當你寫下表達式如{{ val }}時,AngularJS在幕後會爲你在scope模型上設置一個watcher(表達式將被 Angular 編譯成一個監視函數),它用來在數據發生變化的時候更新view。這裏的watcher和你會在AngularJS中設置的watcher是同樣的:瀏覽器
1
2
3
|
$scope.$watch(
'val'
,
function
(newValue, oldValue) {
//update the DOM with newValue
});
|
將數據附加到 Scope 上,數據自身不會對性能產生影響,若是沒有監視器來監視這個屬性,那個這個屬性在不在 Scope 上是無關重要的;Angular 並不會遍歷 Scope 上的屬性,它將遍歷全部的觀察器。
每一個監視函數是在每次 $digest 過程當中被調用的。所以,咱們要注意觀察器的數量以及每一個監視函數或者監視表達式的性能。
當瀏覽器接收到能夠被 angular context 處理的事件時,digest循環就會觸發,遍歷所有的digest循環就會觸發,遍歷全部的watch,最後更新 dom。
舉個栗子
1
|
<
button
ng-click="val=val+1">increase 1</
button
>
|
click 時會產生一次更新的操做(至少觸發兩次 $digest 循環)
按下按鈕
瀏覽器接收到一個事件,進入到 angular context
digest循環開始執行,查詢每個digest循環開始執行,查詢每一個watch 是否變化
因爲監視 scope.val的scope.val的watch 報告了變化,所以強制再執行一次 $digest 循環
新的 $digest 循環未檢測到變化
瀏覽器拿回控制器,更新 $scope. val.新值對應的 dom
在調用了scope.scope.digest()後,digest循環就開始了。假設你在一個ng−click指令對應的handler函數中更改了scope中的一條數據,此時AngularJS會自動地通過調用digest循環就開始了。假設你在一個ng−click指令對應的handler函數中更改了scope中的一條數據,此時AngularJS會自動地經過調用digest()來觸發一輪digest循環。當digest循環。當digest循環開始後,它會觸發每一個watcher。這些watchers會檢查scope中的當前model值是否和上一次計算獲得的model值不一樣。若是不一樣,那麼對應的回調函數會被執行。調用該函數的結果,就是view中的表達式內容(譯註:諸如{{ val }})會被更新。除了ng-click指令,還有一些其它的built-in指令以及服務來讓你更改models(好比ng-model,timeout等)和自動觸發一次timeout等)和自動觸發一次digest循環。
目前爲止還不錯!可是,有一個小問題。在上面的例子中,AngularJS並不直接調用digest(),而是調用digest(),而是調用scope.apply(),後者會調用apply(),後者會調用rootScope.$digest()。所以,一輪digest循環在digest循環在rootScope開始,隨後會訪問到全部的children scope中的watchers。
一般寫代碼時咱們無需主動調用 apply或apply或digest 是由於 angular 在外部對咱們的回調函數作了包裝。例如經常使用的 ng-click,這是一個指令(Directive),內部實現則 相似 於
1
2
3
|
DOM.addEventListener(
'click'
,
function
($scope) {
$scope.$apply(() => userCode());
});
|
能夠看到:ng-click 幫咱們作了 apply這個操做。類似的不只是這些事件回調函數,還有apply這個操做。相似的不僅是這些事件回調函數,還有http、timeout等。我聽很多人抱怨說angular這個庫太大了什麼都管,其實你可以不用它自帶的這些服務(Service),只要你記得手工調用timeout等。我聽不少人抱怨說angular這個庫太大了什麼都管,其實你能夠不用它自帶的這些服務(Service),只要你記得手工調用scope.$apply。
如今,假設你將ng-click指令關聯到了一個button上,並傳入了一個function名到ng-click上。當該button被點擊時,AngularJS會將此function包裝到一個wrapping function中,而後傳入到scope.scope.apply()。所以,你的function會正常被執行,修改models(若是須要的話),此時一輪$digest循環也會被觸發,用來確保view也會被更新。
Note: scope.scope.apply()會自動地調用rootScope.rootScope.digest()。apply()方法有兩種形式。第一種會接受一個function做爲參數,執行該function並且觸發一輪apply()方法有兩種形式。第一種會接受一個function做爲參數,執行該function而且觸發一輪digest循環。第二種會不接受任何參數,只是觸發一輪$digest循環。咱們立刻會看到爲何第一種形式更好。
$digest 循環的上限是 10 次(超過 10次後拋出一個異常,防止無限循環)。
$digest 循環不會只運行一次。在當前的一次循環結束後,它會再執行一次循環用來檢查是否有 models 發生了變化。
這就是髒檢查(Dirty Checking),它用來處理在 listener 函數被執行時可能引發的 model 變化。所以 digest循環會持續運行直到model不再發生變化,或者digest循環會持續運行直到model再也不發生變化,或者digest 循環的次數達到了 10 次(超過 10 次後拋出一個異常,防止無限循環)。
當 $digest 循環結束時,DOM 相應地變化。
angular 會在可能觸發 UI 變動的時候進行髒檢查:這句話並不許確。實際上,
髒檢查是digest執行的,另外一個更經常使用的用於觸發髒檢查的函數apply——其實就是 $digest 的一個簡單封裝(還作了一些抓異常的工做)。
一般寫代碼時咱們無需主動調用 apply或apply或digest 是由於 angular 在外部對咱們的回調函數作了包裝。例如經常使用的 ng-click,這是一個指令(Directive),內部實現則 相似於
1
2
3
|
DOM.addEventListener(
'click'
,
function
($scope) {
$scope.$apply(() => userCode());
});
|
angular對經常使用的dom事件,xhq事件做了封裝,若是調用這些封裝,就會在裏面觸發進入angular的digest流程,主要有如下狀況:
DOM事件,如用戶輸入文本,點擊按鈕等,(ng-click)
XHQ響應事件($http)
瀏覽器Location變動事件,即Url中hash部分變動($location)
Timer事件(Timeout,Timeout,interval)
手動調用apply或apply或digest
apply是apply是scope(或者是 direcvie 裏的 link 函數中的 scope)的一個函數,調用它會強制一次 digest循環(除非當前正在執行循環,這種情況下會拋出一個異常,這是我們不需要在那裏執行digest循環(除非當前正在執行循環,這種狀況下會拋出一個異常,這是咱們不須要在那裏執行apply 的標誌)。
apply()和apply()和digest() 有兩個區別。
1) 最直接的差別是, $apply 能夠帶參數,它能夠接受一個函數,而後在應用數據以後,調用這個函數。因此,通常在集成非 Angular 框架(好比jQuery)的代碼時,能夠把代碼寫在這個裏面調用。
2) 當調用 digest的時候,只觸發當前做用域和它的子做用域上的監控,但是當調用digest的時候,只觸發當前做用域和它的子做用域上的監控,可是當調用apply 的時候,會觸發做用域樹上的全部監控。
取決因而否在 Angular 上下文環境(angular context)。
AngularJS對此有着很是明確的要求,就是它只負責對發生於AngularJS上下文環境中的變動會作出自動地響應(即,在apply()方法中發生的對於models的更改)。AngularJS的built−in指令就是這樣作的,所以任何的model變更都會被反映到view中。但是,如果你在AngularJS上下文之外的任何地方修改了model,那麼你就需要