angular $apply()以及$digest()讲解

重点的东西放上面,说三遍:

记住的最重要的是ng是否能检测到你对于model的修改。如果它不能检测到,那么你就需要手动地调用$apply()!

记住的最重要的是ng是否能检测到你对于model的修改。如果它不能检测到,那么你就需要手动地调用$apply()

记住的最重要的是ng是否能检测到你对于model的修改。如果它不能检测到,那么你就需要手动地调用$apply()

 

 

$apply()$digest()AngularJS中是两个核心概念。为了了解AngularJS的工作方式,首先需要了解$apply()$digest()是如何工作的。

ng中提供了一个很酷的特性叫双向数据绑定(two-way Data Binding),当view和scope中的数据发生变化时会相互自动更新。

ng是如何做到的呢?下面来讲解一下:
当写下表达式{{model}}时,ng在幕后偷偷为你做一件事:

在scope模型上设置一个watcher,用来监听数据发生变化的时候更新view。ng会周期性的运行一个函数来检查scope模型数据是否发生变化。

这时候,$digest就出来发力了。

watcher原理:

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

在$digest循环中,watchers会被触发,当一个watcher被触发时,ng会检测scope模型,如何检测到发生变化,关联到watcher的回调函数就会被调用。

下面问题来了,$digest循环什么时候以什么方式开始呢?

 

在调用了$scope.$digest()后,$digest循环就开始了。举例:

你在一个ng-click指令对应的handler函数中更改了scope中的一条数据,此时AngularJS会自动地通过调用$digest()来触发一轮$digest循环。当$digest循环开始后,它会触发每个watcher。这些watchers会检查scope中的当前model值是否和上一次计算得到的model值不同。如果不同,那么对应的回调函数会被执行。调用该函数的结果,就是view中的表达式内容(译注:诸如{{ model }})会被更新。

 

OK,还有个小注意事项:》》》》》》ng并不会直接调用$digest()。而是调用$scope.$apply(),然后调用$rootScope.$digest()。So,一轮$digest循环在$rootScope开始,随后会访问到所有的children scope中的watchers

 

现在,我们假设button上有个方法fn1:

<button ng-click="fn1()"></button>

当点击时,ng会将fn1包装到wrappering function中,然后传入到$scope.$apply()。因此我的fn1也会被正常执行。

如果点击后数据model数据发生变化,新一轮的$digest loop循环也会被触发。

 

Note: $scope.$apply()会自动地调用$rootScope.$digest()$apply()方法有两种形式:

          第一种会接受一个function作为参数,执行该function并且触发一轮$digest循环;

          第二种会不接受任何参数,只是触发一轮$digest循环。

我们马上会看到为什么第一种形式更好。

 

什么时候手动调用$apply()方法?

如果AngularJS总是将我们的代码wrap到一个function中并传入$apply(),以此来开始一轮$digest循环,那么什么时候才需要我们手动地调用$apply()方法呢?

实际上,AngularJS对此有着非常明确的要求,就是它只负责对发生于AngularJS上下文环境中的变更会做出自动地响应(即,在$apply()方法中发生的对于models的更改)AngularJSbuilt-in指令就是这样做的,所以任何的model变更都会被反映到view中。但是,如果你在AngularJS上下文之外的任何地方修改了model,那么你就需要通过手动调用$apply()来通知AngularJS。这就像告诉AngularJS,你修改了一些models,希望AngularJS帮你触发watchers来做出正确的响应。

 

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

 

来看一个例子:有一个页面,一旦该页面加载完毕了,你希望在两秒钟之后显示一条信息。

你会看到过了2秒钟之后,控制台确实会显示出已经更新model,然而,view并没有更新。

调用$scope.$apply,修改如下:

 

运行了上面的例子,你会看到view在两秒钟之后也会更新。

唯一的变化是我们的代码现在被wrapped到了$scope.$apply()中,它会自动触发$rootScope.$digest(),从而让watchers被触发用以更新view

tips: 顺便提一下,你应该使用$timeout service来代替setTimeout(),因为前者会帮你调用$apply(),让你不需要手动地调用它。

 

$apply()的第二种形式(没有参数):

以上代码也可以在你修改model之后手动调用没有参数的$apply(),也能实现让view更新:

 

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

 

需要记住的是你总是应该使用接受一个function作为参数的$apply()方法。这是因为当你传入一个function$apply()中的时候,这个function会被包装到一个trycatch块中,所以一旦有异常发生,该异常会被$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没有变化。

 

结语

记住的最重要的是ng是否能检测到你对于model的修改。如果它不能检测到,那么你就需要手动地调用$apply()

 

posted on 2016-04-24 12:50  发哥要做活神仙(笔记) 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/91allan/p/5426777.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
AngularJS中,$digest是一个循环机制,用于检查和更新作用域中的所有绑定。它会遍历作用域树,并检查每个作用域中的所有watcher。当检测到一个watcher的值发生变化时,$digest会触发这些watcher的回调函数,从而更新相应的视图。 $digest循环由AngularJS自动触发,不需要手动调用。当AngularJS检测到可能发生变化的事件(例如用户输入、HTTP请求的响应等)时,它会自动启动$digest循环。在这个过程中,AngularJS会检查每个作用域中的watcher,并更新视图以反映最新的模型状态。 然而,有时候我们需要在AngularJS上下文之外修改模型,并希望通知AngularJS进行更新。这时就可以使用$apply方法。$apply方法会触发$digest循环,强制AngularJS检查和更新所有作用域中的watcher。 一种常见的使用场景是在使用第三方库或原生JS代码时,需要手动调用$apply方法来通知AngularJS进行更新。在这种情况下,我们可以将修改模型的代码包裹在$apply函数中,以确保AngularJS能够正确地检测到变化并更新相应的视图。 总结起来,$digestAngularJS自动触发的循环机制,用于检查和更新作用域中的所有绑定。而$apply是手动调用的方法,用于通知AngularJS在AngularJS上下文之外的代码中发生了模型的变化,需要进行更新。 :https://www.jb51.net/article/120399.htm :https://stackoverflow.com/questions/41285152/when-to-call-scope-apply-in-angularjs
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值