淺析$watch ,$apply 和 $digest (Angular篇)

 

前言

瞭解過angular的人都知道,angular的一大特性就是雙向數據綁定。所謂雙向數據綁定,即當View中有任何數據發生了變化,其對應的 scope模型會自動地更新,而當scope模型發生變化時,view中的數據也會更新到最新的值。那麼它是怎麼作到的呢,$watch是怎麼工做的,$apply 和 $digest又是用於作什麼的,下面咱們來探討一下。html

瀏覽器事件和angular擴展

在標準的瀏覽器流程中,當事件被觸發時(好比點擊一個按鈕),瀏覽器會執行該事件的回調函數,執行回調時會進入Javascript執行環境。當回調執行完畢,就退出Javascript執行環境,而後刷新視圖。Angular在此基礎上建立了一個本身的執行環境及事件處理循環,只有在AngularJS執行環境中運行的操做,才能享受到AngularJS提供的數據綁定,異常處理,資源管理等功能和服務。Angular和瀏覽器事件循環交互以下:瀏覽器

 

下面用個例子來解析一下angular執行過程:app

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。spa

(2)AngularJS進入$digest循環。這個循環是由兩個小循環組成的: $evalAsync隊列和$watch列表。執行changeData,changeData中修改了$scope.data;同時,angular遍歷整個$watch列表,檢測到$watch 列表中的data值的變化,而後再次啓動一輪$digest 循環;code

(3)直到檢測到$watch 列表再也不有任何變化後,AngularJS的$digest循環結束,離開AngularJS和Javascript的執行環境。htm

(4)瀏覽器把改變的數據data進行重渲染。對象

$watch

當咱們在UI元素中綁定一個$scope對象時,就會往$watch list裏面添加一個$watch,對於全部綁定給同一$scope對象的UI元素,也只會添加一個$watch到$watch列表中。看以下代碼blog

(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();

相關文章
相關標籤/搜索