理解AngularJs的$apply()和$digest()
来源:互联网 发布:网络品牌营销现状 编辑:程序博客网 时间:2024/05/20 03:47
原文地址:https://www.sitepoint.com/understanding-angulars-apply-digest/
原文作者:Sandeep Panda
$apply()和$digest()是AngularJs的两个核心概念,但同时也是容易令我们感到困惑。为了理解AngularJs的工作原理,我们需要深入理解$apply()和$digest()是如何运作的。这篇文章的主要目的就是解释$apply()和$digest()的概念,而这将在我们的日常AngularJs编程中是非常有用的。
$apply与$digest探险
AngularJS 提供了一个难以置信的、很棒的特性叫做——双向数据绑定,这很大程度上简化了我们的工作。数据绑定的意思是指,当你在页面上作了一些修改时(译者:如你通过输入框输入、下拉框选择等),scope中的一些数据会自动的修改。同样地,当scope中的值修改时,页面视图会自动更新为新的值。AngularJS是如何实现这些的呢?当你写了一个表达式({{aModel}})时,Angular会在幕后启动一个scope模型的观察者,当模型发生变化时依次更新视图。这个观察者跟你在AngularJS中启动的观察者是一样的:
$scope.$watch('aModel', function(newValue, oldValue) { // 用newValue的值来更新页面DOM结构});
观察者启动的就是$digest循环。当一个观察者被启动时,AngularJS会去评估scope模型,并在当它发生变化时调用相应的监听函数。所以,我们接下来的问题就是,$digest循环是合适启动的,如何启动的?
$digest循环的开始是来自于$scope.$digest()方法的调用。假设你通过ng-click指令在处理函数中修改了一个scope中的值,此时,AngularJS通过调用$digest()方法自动触发一个$digest循环。当$digest循环开始时,它会触发每一个观察者,这些观察者回去检查scope当前的值与最后一次计算的值是否不一样。如果是,其对应的监听函数就会执行。除了ng-click以外,还有几个其他的内置directive或service可以让你去修改scope中的model(例如:ng-model,$timeout等)并触发一个$digest循环。
到目前为止还挺好的(原文:So far, so good!)。但是,还有一个小的疑难杂症。在上述例子中,AngularJs并不直接地调用$digest()方法。而是调用$scope.$apply()方法,这会调用$rootScope.$digest()。这样的结果就是,一个$digest循环在$rootScope中开始,随后访问所有的子scope并调用其中的观察者(检查值是否变化,如果变化调用监听函数)。
此时,让我们假设你给一个按钮绑定了一个ng-click指令并传递了一个函数名给它。当这个按钮被点击时,AngularJS用$scope.$apply()包裹这个函数调用。这样,你的函数照常执行,修改model(如果有的话),同时一个$digest循环开启,确保你的改变能够被反映到视图。
注意:$scope.$apply()会自动地调用$rootScope.$digest()方法。$apply()函数有两种形式。第一种传递一个函数作为参数,评估这个函数,并触发一个$digest循环。第二种方式不传递任何参数,被调用时只是开始一个$digest循环。我们在后面很快会发现为什么前者是首选的方法。
该何时手动调用$apply()?
AngularJS通常把我们的代码包裹在$apply()方法里,并开启一个$digest循环,那我们什么时候需要手动去调用$apply()方法吗?事实上,AngularJS中这很清晰。它只负责那些在AngularJS的上下文环境里面发生的model变化(换言之,包裹在$apply()里面的model变化)。AngularJS的内置指令已经实现了这些,所以你做的任何model变化都将被反映到视图。然而,你在AngularJS上下文环境外修改model的话,你就需要通过手动调用$apply()方法来告知Angular这些model变化。就好像是告诉Angular,你在修改某些model,并且这应该要触发那些观察者,这样你的修改就可以正确的传播。
举个例子,如果你用JavaScript原生的setTimeout()函数来修改一个scope中的model,Angular没有办法知道你已经做了修改。在这里你就需要手动地调用能够触发一个$digest循环的$apply()方法。同样,如果你有一个自定义指令设置了一个DOM事件监听,并在处理函数中修改了一些model的值,你需要调用$apply()方法来确保这些修改能够起作用。
让我们来看一个例子。假设你有一个页面,你想在页面加载完成之后延迟两秒显示一个信息。你的实现代码就跟下面的JavaScript和HTML相似。
HTML:
<body ng-app="myApp"> <div ng-controller="MessageController"> Delayed Message: {{message}} </div> </body>JavaScript:
/* 这是没有用$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(); });结果:
(译者:console控制台有打印出信息,但页面上没有显示出信息)
通过运行这个例子,你会发现,延迟函数两秒执行,并修改scope中的模型message($scope.message),而视图却没有修改。这个原因你应该能猜到,那就是我们完了手动地调用$apply()方法。所以,我们需要向下面一样修改我们的getMessage()方法。
JavaScript:
/* 这是用$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(); });结果:
(译者:console控制台有打印信息,视图也有显示信息)
如果你运行这个修改后的例子,你会发现视图在两秒后发生了修改。唯一的变化就是我们的代码被包裹在能够自动触发$rootScope.$digest()方法的$scope.$apply()方法里面。结果观察者照常被触发并且视图发生变化。
注意:顺便提一下,你应该尽可能用$timeout服务,这是自动带有$apply()的倒计时方法(setTimeout()),这样你就不必手动去调用$apply()方法。
还有,在上述的代码中你可照常完成model修改,然后在最后加一个$apply()(没有参数的形式)调用。看一下下面的代码片段:
$scope.getMessage = function() { setTimeout(function() { $scope.message = 'Fetched after two seconds'; console.log('message:' + $scope.message); $scope.$apply(); // 这会触发一个$digest循环 }, 2000);};
$digest循环运行几次?
当一个$digest循环运行时,观察者会被执行去检查scope的model是否已经发生了变化。一旦发生变化,相应的监听器函数就会被调用。这带来了一个重要的问题,如果一个监听器函数自身修改了scope的model值呢?AngularJS将会对这个变回作何反应?
答案是,$digest循环并不仅仅运行一次。在当前循环的最后,它会从头再一次启动来检查是否有任何的model被改变了。这是基本的脏值监测,这么做是为了对任何发生在监听器函数处理中的model变化起作用。这样,$digest循环会一直循环,直到没有model发生变化,或者达到了最大循环次数10。最好保持幂等(stay idempotent),尽量减少在监听器函数中进行model变化。
注意:$digest会至少循环两次,即时你的监听器函数没有修改任何model。如上所述,它会多运行一次来确认model是稳定的、没发生变化的。
结论
我希望这篇文章能够解释清楚关于$apply和$digest的全部。最重要的是要清楚,Angular能否侦查到你做的修改。如果不能,你必须要手动调用$apply()方法。
0 0
- 理解AngularJs的$apply()和$digest()
- AngularJS的digest循环和$apply
- AngularJS中$digest和$apply
- angularJS--apply() 、digest()和watch()方法
- angularJS<五、$scope中的$apply和$digest>
- AngularJS学习之$digest循环和$apply
- [AngularJs问题] $digest $apply
- 理解Angular的$ apply()和$ digest()
- 理解Angular中的$digest()和$apply()
- 理解Angular中的$apply()和$digest()
- 理解Angular中的$apply()和$digest()
- 理解 Angular 中的 $digest() 和 $apply()
- angualr-$apply,$digest,$watch的理解
- AngularJs学习笔记(3)--$scope中的$apply和$digest方法
- AngularJS: $watch() , $digest() and $apply()
- angular.js的$apply 和 $digest()
- 理解$watch ,$apply 和 $digest --- 理解数据绑定过程
- 理解$watch ,$apply 和 $digest --- 理解数据绑定过程
- 第九章 IP选项处理(我再去看IP选项的那些基础概念再来看)
- 理解TCP序列号(Sequence Number)和确认号(Acknowledgment Number)
- 常用微信集成(第三方sdk集成套路)-- 微信分享
- 2016-09-01新的一天,继续加油努力!
- 暗通道优先的图像去雾算法(上)
- 理解AngularJs的$apply()和$digest()
- Shell学习笔记
- Android build system & Android.mk 规范
- 公司内部 action调用持久层规范
- Verilog中阻塞与非阻塞语句
- 【三星官方教程】如何为Gear VR开发应用(六):加入渐变效果
- sublime text用法
- 技术-右下角出现弹框
- HTML转义字符大全