今天突然就想寫寫$digest和$apply,這些都是臟值檢查的主體內容。
先以普通js來做一個簡單的監控例子吧:
var div = ducoment.getElementById("myDiv"); div.addEventListener("click",function(e){ console.log(e); })
在這里,我們給一個div綁定了個點擊事件,並且給這個綁定事件一個回調函數。這里我們就是給了這個div一個監聽,當監聽到有點擊事件發生在此div上,則執行該回調函數。
Angular的$watch也是這樣的效果。拿ngModel為例:
in html: <input ng-model="value" />
in controller.js: $scope.value = "Hello World";
我們只要改變input里的值,在controller里對應的$scope.value也會隨之發生相同的變化。這里就是給$watch列表添加了一個監控函數,從而做到這個效果的。
$watch列表就是給所有綁定到同一$scope對象的UI元素添加一個監控函數到$watch列表里。
$watch列表會觸發$digest循環,並且在$digest循環中通過“臟值檢查”機制進行解析。
臟值檢查
什么是臟值檢查?簡單來說,就是Angular檢查模型的值是否發生了變化,而程序還沒對該變化進行同步的機制。Angular將會通過臟值檢查遍歷整個$watch列表,只要當中的某個值發生了變化,應用就會退回到$digest循環中,直到檢測到這個值不再發生任何變化,然后再啟用新值並且繼續遍歷$watch列表。整個$watch列表循環后,且其中值都不再變化了,那么整個應用程序的model也就趨於穩定了,這時候才在View渲染該有的數據。
這里有個報錯需要注意,也是挺多人會遇到且提出的問題,到底是什么錯誤呢,發上來秀秀,想必大家不會陌生~
Error: [$rootScope:infdig] 10 $digest() iterations reached. Aborting!
$digest循環運行10次,Angular就會拋出這個異常,同時停止$digest循環。而10這個次數可以在config里面注入$rootScopeProvider服務並且配置:
$rootScopeProvider.digestTtl(15);
這時候,錯誤就變成了:
Error: [$rootScope:infdig] 15 $digest() iterations reached. Aborting!
哈哈,小伙伴們肯定會說:你個小賤人,不就是換個數值重新報錯么.... 好吧,那么你上去看看本獸說的出現這個錯誤的原因,不就可以根據問題來解決了么 -。-
上面那個服務是用於配置當值不穩定的時候,$digest循環幾次自動跳出循環的,部分場景可能會用得到。
關於$watch函數以及相關的使用,可以移步本獸的另一篇文章,url請狂戳這里:$watch
$digest循環是臟值檢查機制的主體,那么$digest怎么玩呢?
自動進入$digest我們已經知道了,比如$scope的$watch列表中值發生變化,則觸發$digest循環。
那么手動召喚我們偉大的$digest循環呢?我們可以直接召喚$digest (#01),也可以集齊一只$apply來召喚$digest。
$apply()函數在框架外部讓表達式在Angular上下文內部執行。好吧,這個確實聽起來比較繞...
下面來一段$apply的使用代碼吧:
<div ng-app="Demo" ng-controller="testCtrl as ctrl"> <my-dir count="ctrl.count"></my-dir>{{ctrl.count}} </div>
(function(){ angular.module('Demo', []) .directive("myDir",myDir) .controller('testCtrl',testCtrl); function myDir(){ return{ restrict:'AECM', template:'<input type="button" value="click me" />', scope:{count:"="}, link: function(scope, element, attrs) { element.bind('click', function() { scope.count++; scope.$apply(); }); } } }; function testCtrl() { this.count = 0; } }());
順便也扯下,該在啥時使用$apply
Angular提供的可用於視圖中任意指令都可調用$apply(),比如:ng-click,ng-change等等。
還有一些Angular內置的服務會調用$digest()。比如:$http服務(在XHR請求完成並觸發更新返回的promise中會調用$apply())、使用$timeout()。
在我們使用第三方框架時或在自己寫的指令當中使用。比如:jQuery(Angular不建議在controller內操作DOM,DOM操作應該在Directive中使用,所以jQuery代碼也請寫在指令里吧),上面的代碼(也是在指令中使用)。
關於$apply vs $digest ,請移步另一篇Github譯文:( 譯、持續更新 ) JavaScript小技巧介紹 #01 - AngularJs: $digest vs $apply