明Angular中之$apply()以及$digest()

$apply()和$digest()在AngularJS中凡是个别独中心概念,但是有时它以受丁困惑。而以了解AngularJS的劳作措施,首先需了解$apply()和$digest()是怎样做事之。这首文章旨在解释$apply()和$digest()是啊,以及当日常的编码中争运用它们。

 

探索$apply()和$digest()

AngularJS提供了一个万分好的特性叫做双向数据绑定(Two-way Data Binding),这个特点大大简化了俺们的代码编写方式。数据绑定意味着当View中生任何数据产生了变化,那么这转变呢会自行地反映及scope的多少上,也即表示scope模型会自行地翻新。类似地,当scope模型发生变化时,view中之数目也会见更新到新型的价值。那么AngularJS是怎形成及时一点之啊?当您勾勒下表达式如{{
aModel }}时,AngularJS在悄悄会吧而在scope模型上安装一个watcher,它用来当多少发生变化的上更新view。这里的watcher和公晤面于AngularJS中设置的watcher是同等的:

 

[javascript] view
plain copy

 

  1. $scope.$watch(‘aModel’, function(newValue, oldValue) {  
  2.   //update the DOM with newValue  
  3. });  

 

传到$watch()中之老二单参数是一个回调函数,该函数在aModel的值发生变化的早晚会为调用。当aModel发生变化的当儿,这个回调函数会于调用来更新view这或多或少不难理解,但是,还留存一个生重点之题材!AngularJS是安了解啊时候如果调用这个回调函数呢?换句话说,AngularJS是怎么知晓aModel发生了变通,才调用了对应的回调函数呢?它会周期性的运作一个函数来检查scope模型中之数额是否有了变化呢?好吧,这便是$digest循环的用武之地了。

 

在$digest循环中,watchers会被触发。当一个watcher被点时,AngularJS会检测scope模型,如何它发出了转那么涉及到拖欠watcher的回调函数就会于调用。那么,下一个题材就是$digest循环是以啊时盖各种办法开始的?

 

以调用了$scope.$digest()后,$digest循环就从头了。假设你在一个ng-click指令对应之handler函数中改了scope中的同样长达数,此时AngularJS会自动地由此调用$digest()来点发一样车轮$digest循环。当$digest循环开始后,它会硌每个watcher。这些watchers会检查scope中的眼前model值是否跟上亦然糟计算得到的model值不同。如果差,那么相应的回调函数会叫实施。调用该函数的结果,就是view中之表达式内容(译注:诸如{{ aModel
}})会吃更新。除了ng-click指令,还有部分旁的built-in指令和劳动来深受您转移models(比如ng-model,$timeout等)和电动触发一涂鸦$digest循环。

 

目前为止还不易!但是,有一个多少题目。在上头的例子中,AngularJS并无直接调用$digest(),而是调用$scope.$apply(),后者会调用$rootScope.$digest()。因此,一车轮$digest循环在$rootScope开始,随后会看到具备的children scope中的watchers。

 

如今,假要你拿ng-click指令关联到了一个button上,并传播了一个function名到ng-click上。当该button被点击时,AngularJS会将此function包装至一个wrapping function中,然后传入到$scope.$apply()。因此,你的function会正常为实施,修改models(如果需要的话),此时同等轮子$digest循环也会见给点,用来担保view也会受更新。

 

Note: $scope.$apply()会自行地调用$rootScope.$digest()。$apply()方法发生零星种植样式。第一种植会承受一个function作为参数,执行该function并且触发一轮$digest循环。第二种植会无接受其他参数,只是触发一车轮$digest循环。我们这会看为什么第一栽形式重新好。

 

哎呀时候手动调用$apply()方法?

要AngularJS总是拿我们的代码wrap到一个function中并传播$apply(),以这来起同车轮$digest循环,那么什么时才要我们手动地调用$apply()方法吧?实际上,AngularJS对是负有非常明白的求,就是它不过承担对产生于AngularJS上下文环境面临的变更会做出自动地应(即,在$apply()方法中出的对于models的转移)。AngularJS的built-in指令就是这么做的,所以任何的model变更还见面受反映至view中。但是,如果你在AngularJS上下文之外的别地方改了model,那么你就待通过手动调用$apply()来通知AngularJS。这就是如告诉AngularJS,你改改了一些models,希望AngularJS帮您触发watchers来做出正确的响应。

 

比如,如果您以了JavaScript中之setTimeout()来更新一个scope model,那么AngularJS就从不艺术知道乃转移了啊。这种气象下,调用$apply()就是公的权责了,通过调用它来点发一样车轮$digest循环。类似地,如果您生一个发令用来安一个DOM事件listener并且以拖欠listener中改了部分models,那么你吗待通过手动调用$apply()来担保变更会被科学的体现到view中。

 

叫咱来拘禁一个例。加入你生出一个页面,一旦该页面加载了了,你愿意以少数秒钟后显得同一久消息。你的兑现可能是下面这个法的:

 

HTML:

 

[html] view
plain copy

 

  1. <body ng-app=”myApp”>  
  2.   <div ng-controller=”MessageController”>  
  3.     Delayed Message: {{message}}  
  4.   </div>    
  5. </body>  

 

 

JavaScript:

[javascript] view
plain copy

 

  1. /* What happens without an $apply() */  
  2.       
  3.     angular.module(‘myApp’,[]).controller(‘MessageController’, function($scope) {  
  4.       
  5.       $scope.getMessage = function() {  
  6.         setTimeout(function() {  
  7.           $scope.message = ‘Fetched after 3 seconds’;  
  8.           console.log(‘message:’+$scope.message);  
  9.         }, 2000);  
  10.       }  
  11.         
  12.       $scope.getMessage();  
  13.       
  14.     });  

 

由此运行是例子,你晤面看了了个别秒钟后,控制台确实会来得出已更新的model,然而,view并不曾更新。原因想必你曾亮了,就是我们忘记了调用$apply()方法。因此,我们得改getMessage(),如下所示:

 

 

[javascript] view
plain copy

 

  1. /* What happens with $apply */   
  2. angular.module(‘myApp’,[]).controller(‘MessageController’, function($scope) {  
  3.       
  4.       $scope.getMessage = function() {  
  5.         setTimeout(function() {  
  6.           $scope.$apply(function() {  
  7.             //wrapped this within $apply  
  8.             $scope.message = ‘Fetched after 3 seconds’;   
  9.             console.log(‘message:’ + $scope.message);  
  10.           });  
  11.         }, 2000);  
  12.       }  
  13.         
  14.       $scope.getMessage();  
  15.       
  16.     });  

 

 

倘你运行了端的例证,你见面见到view在片秒钟后吧会更新。唯一的浮动是咱们的代码现在被wrapped到了$scope.$apply()中,它会活动触发$rootScope.$digest(),从而让watchers被硌用以更新view。

 

Note:顺便取一下,你当用$timeout service来替代setTimeout(),因为前端会支援您调用$apply(),让您无欲手动地调用它。

 

还要,注意在以上的代码中您呢得以于改了model之后手动调用没有参数的$apply(),就如下这样:

 

[javascript] view
plain copy

 

  1. $scope.getMessage = function() {  
  2.   setTimeout(function() {  
  3.     $scope.message = ‘Fetched after two seconds’;  
  4.     console.log(‘message:’ + $scope.message);  
  5.     $scope.$apply(); //this triggers a $digest  
  6.   }, 2000);
  7. };  

 

上述的代码用了$apply()的亚种样式,也就是没有参数的款型。需要牢记的凡你连应该以接受一个function作为参数的$apply()方法。这是以当你传入一个function到$apply()中的当儿,这个function会被包至一个try…catch块中,所以如果产生那个来,该大会为$exceptionHandler service处理。

 

$digest循环会运行AngularJS多少坏?

当一个$digest循环运行时,watchers会被执行来检查scope中之models是否生了变通。如果生了变通,那么相应的listener函数就见面吃实施。这提到到一个着重之问题。如果listener函数本身会修改一个scope model呢?AngularJS会怎么处理这种景象?

 

答案是$digest循环不会见独自运行一不行。在脚下之一模一样不善巡回结束晚,它见面还实施同样糟糕循环用来检查是否来models发生了转。这便是水污染检查(Dirty Checking),它之所以来处理在listener函数被执行时或许滋生的model变化。因此,$digest循环会持续运作直到model不再发生变化,或者$digest循环的次数及了10差。因此,尽可能地不用当listener函数中修改model。

 

Note: $digest循环最少也会运作两糟,即使在listener函数中并从未转任何model。正使上面讨论的那样,它会多运行一不好来确保models没有变化。

 

结语

自家愿意马上篇稿子解释清楚了$apply和$digest。需要牢记的无比关键的凡AngularJS是否能够检测及您对model的改动。如果其不可知检测及,那么您尽管需手动地调用$apply()。

 

 

原稿地址

相关文章