標籤:images 隊列 檢查 也會 互動 put facebook 不能 img
前言
瞭解過angular的人都知道,angular的一大特性就是雙向資料繫結。所謂雙向資料繫結,即當View中有任何資料發生了變化,其對應的 scope模型會自動地更新,而當scope模型發生變化時,view中的資料也會更新到最新的值。那麼它是怎麼做到的呢,$watch是怎麼工作的,$apply 和 $digest又是用於做什麼的,下面我們來探討一下。
瀏覽器事件和angular擴充
在標準的瀏覽器流程中,當事件被觸發時(比如點擊一個按鈕),瀏覽器會執行該事件的回呼函數,執行回調時會進入Javascript執行環境。當回調執行完畢,就退出Javascript執行環境,然後重新整理視圖。Angular在此基礎上建立了一個自己的執行環境及事件處理迴圈,只有在AngularJS執行環境中啟動並執行操作,才能享受到AngularJS提供的資料繫結,異常處理,資源管理等功能和服務。Angular和瀏覽器事件迴圈互動如下:
下面用個例子來解析一下angular執行過程:
html
<button ng-click="changeData()">點擊</button><p>{{data}}</p>
controller
$scope.changeData=function(){ $scope.data=1;}
(1)上面的“點擊”按鈕綁定到angular的點擊事件,當使用者點擊按鈕時,angular會把changeData函數封裝並傳入到$scope.$apply(),通過調用$scope.$apply(changeData),進入到angular執行環境,在angular環境中執行changeData。
(2)AngularJS進入$digest迴圈。這個迴圈是由兩個小迴圈組成的: $evalAsync隊列和$watch列表。執行changeData,changeData中修改了$scope.data;同時,angular遍曆整個$watch列表,檢測到$watch 列表中的data值的變化,然後再次啟動一輪$digest 迴圈;
(3)直到檢測到$watch 列表不再有任何變化後,AngularJS的$digest迴圈結束,離開AngularJS和Javascript的執行環境。
(4)瀏覽器把改變的資料data進行重渲染。
$watch
當我們在UI元素中綁定一個$scope對象時,就會往$watch list裡面添加一個$watch,對於所有綁定給同一$scope對象的UI元素,也只會添加一個$watch到$watch列表中。看如下代碼
(1)
html
<input ng-model="name" type="text" placeholder="Your name"><input ng-model="age" type="text" placeholder="Your age"><h1>Hello {{name}}</h1>
這裡有兩個一樣的$scope.name,還有個$scope.pass,所以在$watch list裡面加入兩個$watch。
(2)
html
<p>{{a}}</p>
controller
$scope.a=‘dataA‘;$scope.a=‘dataB‘;
這裡雖然在controller中定義了兩個$scope對象,但是只有一個$scope.a是綁定到UI元素中的,所以只在$watch list裡面加入一個$watch。
$digest
當瀏覽器接收到可以被angular 執行環境處理的事件時(比如ng-click、ng-keypress),$digest迴圈就會觸發。這個迴圈是由兩個小的迴圈組合起來的。一個處理evalAsync隊列,另一個處理$watch隊列。
在$digest迴圈中,angular會遍曆完整個$watch列表,所有的$watch都檢查完後只要有任何一個$watch的值發生變化,這個迴圈就會再次觸發,繼續遍曆$watch列表直到檢測到不再有任何變化。為什麼要再次運行這一迴圈?因為如果你更新了$watch列表中某個用於更新另一個值的值,Angular將檢測不到更新,除非再次運行這個迴圈。看如下例子
html
<button ng-click="changeNum()">點擊</button><p>{{num1}}</p><p>{{num2}}</p>
controller
$scope.num1=1;$scope.num2=$scope.num1+2;$scope.changeNum=function(){ $scope.num1=2;}
當我們點擊按鈕的時候,改變了num1的資料,由於num2資料是受num1資料影響的,如果不再次啟動$digest迴圈,Angular將檢測不到num2資料的更新。
這就是所謂的髒值檢測。這樣就能夠保證每個model都已經不會再變化。記住如果迴圈超過10次的話,它將會拋出一個異常,防止無限迴圈。 當$digest迴圈結束時,DOM相應地變化。
$apply
$apply()函數可以使事件進入到Angular的執行環境中執行。那麼問題來了,什麼時候該用$apply(),什麼時候不該用呢?
Angular提供的可用於視圖中的任意指令(比如ng-click、ng-keypress)、Angular內建的服務(比如$http、$timeout等),使用的時候都會自動調用$apply()。所以,我們不用再去手動調用$apply()了;
當我們手動處理事件,使用第三方架構(比如jQuery、Facebook API),或者調用setTimeout(),他們並沒有調用$apply(),所以事件不能在angular執行環境中執行,$digest迴圈無法檢測到事件中對視圖資料的更改,視圖無法更新。這個時候,我們就需要手動調用$apply();
淺析$watch ,$apply 和 $digest (Angular篇)