關鍵詞:$watch, $digest, $apply, dirty-checking
瀏覽器持續等待例如用戶交互這樣的事件。當你在一個<input>
標籤裏輸入字符以後,這個事件的回調函數在JS解釋器中執行了其包含的DOM操做,執行完畢後,瀏覽器響應地對DOM作出了變化。Angular拓展了這個事件循環,使它有時候成爲angular context
的執行環境。html
$watch
能夠檢測model的變化。每當綁定一個數據到view上的時候,$watch
隊列就會插入一條對應的$watch
。例子以下:jquery
app.controller('MainCtrl', function($scope) { $scope.people = [...]; // 假設長度爲10 });
<ul> <li ng-repeat="person in people"> {{person.name}} - {{person.age}} </li> </ul>
其中ng-repeat
生成了一個1個$watch
,每一個person生成了2個$watch
,總共是(1+2*10),21個$watch
。$watch
的生成階段是模板加載完成,也就是linking
階段。(angular分爲compile
和linking
階段),Angular會尋找每一個directive(上面的例子中ng-repeat和{{}}都屬於directive),而後生成每一個$watch
。瀏覽器
當瀏覽器接收到angular context相關的事件時,$digest
循環就會被觸發。它由2個小循環組成,1個處理evalAsync
隊列,另外一個處理$watch
隊列。$digest
進行循環時,將遍歷$watch
隊列,查看是否有數據更新過,這種遍歷就叫作dirty-checkin
(髒檢查),若是髒檢查發現有$watch
更新,將會觸發新的髒檢查,直到全部的$watch
都沒有更新。這樣就能保證每一個model都不會變化。app
髒檢查超過10次後會拋出異常防止無限循環。$digest
循環結束後DOM會相應地發生變化。其實$digest
從字面意義理解就像「消化」的過程同樣,逐漸地把全部養分($watch
的變化)都吸取掉。函數
$apply
決定事件是否進入angular context
,使用angualr的自帶directive,好比ng-model
,更改綁定的數據時,angular會將事件封裝到$apply
中。好比,ng-model="name"
的輸入框,輸入字符「w」,事件會調用,$apply("name='w';")
,完成$scope
中的數據更新。
調用第三方庫時的數據綁定
當在angular中調用jquery,並不能更新jquery綁定的數據,由於jquery沒有調用$apply
,事件沒有進入angular context
,致使$digest
沒有執行。例子以下:spa
app.directive('clickable', function() { return { restrict: "E", scope: { count1: '=', count2: '=' }, template: '<ul style="background-color: lightblue"><li>{{count1}}</li><li>{{count2}}</li></ul>', link: function(scope, element, attrs) { element.bind('click', function() { scope.count1++; scope.count2++; }); } } }); app.controller('MainCtrl', function($scope) { $scope.count1= 0; $scope.count2= 0; });
例子中,每次點擊該元素,預期count1和count2都會自增1,但實際沒有。其實$scope
(ViewModel)已經改變,可是沒有強制執行$digest
。修改click事件以下:rest
element.bind('click', function() { scope.$apply(function() { scope.foo++; scope.bar++; }); })
通過調用$apply
實現了預期。code
angular事件綁定機制以下圖:htm