理解Angular中的$apply()以及$digest()

本文深入解析AngularJS中的apply()和digest(),包括它们的作用、工作原理及在编码中的应用,帮助开发者更好地理解和利用这两个核心概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分类: AngularJS JavaScript 2014-08-20 13:49 13320人阅读 评论(3) 收藏 举报
angularjsjavascriptangular
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 plaincopyprint?
scope.watch(‘aModel’, function(newValue, oldValue) {
//update the DOM with newValue
});

传入到watch()aModelaModelviewAngularJSAngularJSaModelscopedigest循环的用武之地了。

digestwatcherswatcherAngularJSscopewatcherdigest循环是在什么时候以各种方式开始的?

在调用了scope.digest()后,digestngclickhandlerscopeAngularJSdigest()来触发一轮digestdigest循环开始后,它会触发每个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()functionfunctiondigest循环。第二种会不接受任何参数,只是触发一轮$digest循环。我们马上会看到为什么第一种形式更好。

什么时候手动调用apply()AngularJSwrapfunctionapply(),以此来开始一轮digestapply()方法呢?实际上,AngularJS对此有着非常明确的要求,就是它只负责对发生于AngularJS上下文环境中的变更会做出自动地响应(即,在apply()models)AngularJSbuiltinmodelviewAngularJSmodelapply()来通知AngularJS。这就像告诉AngularJS,你修改了一些models,希望AngularJS帮你触发watchers来做出正确的响应。

比如,如果你使用了JavaScript中的setTimeout()来更新一个scope model,那么AngularJS就没有办法知道你更改了什么。这种情况下,调用apply()digest循环。类似地,如果你有一个指令用来设置一个DOM事件listener并且在该listener中修改了一些models,那么你也需要通过手动调用$apply()来确保变更会被正确的反映到view中。

让我们来看一个例子。加入你有一个页面,一旦该页面加载完毕了,你希望在两秒钟之后显示一条信息。你的实现可能是下面这个样子的:

  1. 1.
HTML: [html] view plaincopyprint? <body ng-app="myApp">     <div
    ng-controller="MessageController">  
        Delayed Message: {{message}}     </div>     </body>  

    JavaScript: [javascript] view plaincopyprint? /* What happens
    without an $apply() */  

        angular.module('myApp',[]).controller('MessageController', function($scope) {  

          $scope.getMessage = function() {  
            setTimeout(function() {  
              $scope.message = 'Fetched after 3 seconds';  
              console.log('message:'+$scope.message);  
            }, 2000);  
          }  

          $scope.getMessage();  

        });

通过运行这个例子,你会看到过了两秒钟之后,控制台确实会显示出已经更新的model,然而,view并没有更新。原因也许你已经知道了,就是我们忘了调用$apply()方法。因此,我们需要修改getMessage(),如下所示:

[javascript] view plaincopyprint?
/* What happens with $apply */   
angular.module('myApp',[]).controller('MessageController', function($scope) {  

      $scope.getMessage = function() {  
        setTimeout(function() {  
          $scope.$apply(function() {  
            //wrapped this within $apply  
            $scope.message = 'Fetched after 3 seconds';   
            console.log('message:' + $scope.message);  
          });  
        }, 2000);  
      }  

      $scope.getMessage();  

    });  

如果你运行了上面的例子,你会看到view在两秒钟之后也会更新。唯一的变化是我们的代码现在被wrapped到了scope.apply()中,它会自动触发rootScope.digest(),从而让watchers被触发用以更新view。

Note:顺便提一下,你应该使用timeoutservicesetTimeout()apply(),让你不需要手动地调用它。

而且,注意在以上的代码中你也可以在修改了model之后手动调用没有参数的$apply(),就像下面这样:

[javascript] view plaincopyprint?
$scope.getMessage = function() {  
  setTimeout(function() {  
    $scope.message = 'Fetched after two seconds';  
    console.log('message:' + $scope.message);  
    $scope.$apply(); //this triggers a $digest  
  }, 2000);  
};  

以上的代码使用了apply()使functionapply()方法。这是因为当你传入一个function到apply()functiontrycatchexceptionHandler service处理。

digestdigest循环运行时,watchers会被执行来检查scope中的models是否发生了变化。如果发生了变化,那么相应的listener函数就会被执行。这涉及到一个重要的问题。如果listener函数本身会修改一个scope model呢?AngularJS会怎么处理这种情况?

答案是digestmodels(DirtyChecking)listenermodeldigest循环会持续运行直到model不再发生变化,或者$digest循环的次数达到了10次。因此,尽可能地不要在listener函数中修改model。

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

结语
我希望这篇文章解释清楚了applydigest。需要记住的最重要的是AngularJS是否能检测到你对于model的修改。如果它不能检测到,那么你就需要手动地调用$apply()。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值