理解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是一样的:

$scope.$watch('aModel', function(newValue, oldValue) {
  //update the DOM with newValue
});

传入到$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:

<body ng-app="myApp">
  <div ng-controller="MessageController">
    Delayed Message: {{message}}
  </div>
</body>

JavaScript:

/* 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(),例如以下所看到的:

/* 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:顺便提一下,你应该使用$timeout service来取代setTimeout(),由于前者会帮你调用$apply(),让你不须要手动地调用它。

并且,注意在以上的代码中你也能够在改动了model之后手动调用没有參数的$apply()。就像以下这样:

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

以上的代码使用了$apply()的另外一种形式,也就是没有參数的形式。

须要记住的是你总是应该使用接受一个function作为參数的$apply()方法。

这是由于当你传入一个function到$apply()中的时候。这个function会被包装到一个try…catch块中。所以一旦有异常发生,该异常会被$exceptionHandler
service处理。

$digest循环会执行多少次?

当一个$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()。

原文地址

http://www.sitepoint.com/understanding-angulars-apply-digest/

时间: 2024-10-02 22:09:18

理解Angular中的$apply()以及$digest()的相关文章

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

reference:http://blog.csdn.net/dm_vincent/article/details/38705099 $apply()和$digest()在AngularJS中是两个核心概念,但是有时候它们又让人困惑.而为了了解AngularJS的工作方式,首先需要了解$apply()和$digest()是如何工作的.这篇文章旨在解释$apply()和$digest()是什么,以及在日常的编码中如何应用它们. 探索$apply()和$digest() AngularJS提供了一个

[email&#160;protected]理解Angular中的$apply()以及$digest()

$apply() 和 $digest() 在 AngularJS 中是两个核心概念,但是有时候它们又让人困惑.而为了了解 AngularJS 的工作方式,首先需要了解 $apply() 和 $digest() 是如何工作的.这篇文章旨在解释 $apply() 和 $digest() 是什么,以及在日常的编码中如何应用它们. 探索 $apply() 和 $digest() AngularJS 提供了一个非常酷的特性叫做双向数据绑定 (Two-way Data Binding) ,这个特性大大简化了

通俗理解angularjs中的$apply,$digest,$watch

<!DOCTYPE html> <html lang="zh-CN" ng-app="app"> <head> <meta charset="utf-8"> <title>angular时钟辅助理解$apply,$digest,$watch</title> <link rel="stylesheet" href="../bootstrap.

(七)理解angular中的module和injector,即依赖注入

依赖注入(DI)的好处不再赘言,使用过spring框架的都知道.angularjs作为前台js框架,也提供了对DI的支持,这是javascript/jquery不具备的特性.angularjs中与DI相关有angular.module().angular.injector(). $injector.$provide.对于一个DI容器来说,必须具备3个要素:服务的注册.依赖关系的声明.对象的获取.比如spring中,服务的注册是通过xml配置文件的<bean>标签或是注解@Repository.

理解angular中的module和injector,即依赖注入

依赖注入(DI)的好处不再赘言,使用过spring框架的都知道.angularjs作为前台js框架,也提供了对DI的支持,这是javascript/jquery不具备的特性.angularjs中与DI相关有angular.module().angular.injector(). $injector.$provide.对于一个DI容器来说,必须具备3个要素:服务的注册.依赖关系的声明.对象的获取.比如spring中,服务的注册是通过xml配置文件的<bean>标签或是注解@Repository.

理解javascript中call/apply的使用和模拟实现

call/apply 的作用 call() 方法调用一个函数, 其具有一个指定的this值和分别地提供的参数. 注意:该方法的作用和 apply() 方法类似,只有一个区别,就是call()方法接受的是若干个参数的列表,而apply()方法接受的是一个包含多个参数的数组. function sayName(age) { // this.name = "person"; (1) console.log(this.name,age); } const student = { name: &

AngularJS中的$watch(),$digest()和$apply()

AngularJS $scope里面的$watch(),$digest()和$apply()是AngularJS的核心函数,学习AngularJS必须理解这几个函数. 在绑定$scope中的变量到view的时候,AngularJS自动在内部创建一个"Watch"."Watch"用于监听AngularJS scope中变量的改变.可以通过调用$scope.$watch()这个方法来创建"Watch". $scope.$digest()函数会循环访问

谈谈Angular关于$watch,$apply 以及 $digest的工作原理

这篇文章主要是面向那些刚开始学AngularJs和想要了解数据绑定(data-binding)是怎么工作的, 如果你已经熟悉如何使用angularjs了,我强烈建议你不用阅读了. angularjs使用者想要知道data-binding是如何工作的,就会遇到很多的关的术语 比如$wacth,$apply,$digest,dirty-checking(脏值检测)...等等,这些又是做什么的呢? 在这篇文章里我会解决所有的疑问,通过结合这些术语在一起来学习. 但是我会尽量用简单的方式来说明. 现在开

[转]理解$watch ,$apply 和 $digest --- 理解数据绑定过程

原文地址:http://angular-tips.com/blog/2013/08/watch-how-the-apply-runs-a-digest/ 注 这篇博文主要是写给新手的,是给那些刚刚开始接触Angular,并且想了解数据帮定是如何工作的人.如果你已经对Angular比较了解了,那强烈建议你直接去阅读源代码. Angular用户都想知道数据绑定是怎么实现的.你可能会看到各种各样的词汇:$watch,$apply,$digest,dirty-checking... 它们是什么?它们是如