您的位置:首页 > 移动开发

Web前端之:浅析$watch ,$apply 和 $digest (Angular篇)

2017-03-31 00:00 85 查看
摘要: 了解过angular的人都知道,angular的一大特性就是双向数据绑定。

前言
了解过angular的人都知道,angular的一大特性就是双向数据绑定。所谓双向数据绑定,即当View中有任何数据发生了变化,其对应的 scope模型会自动地更新,而当scope模型发生变化时,view中的数据也会更新到最新的值。那么它是怎么做到的呢,$watch是怎么工作的,$apply 和 $digest又是用于做什么的,下面我们来探讨一下。

浏览器事件和angular扩展
在标准的浏览器流程中,当事件被触发时(比如点击一个按钮),浏览器会执行该事件的回调函数,执行回调时会进入Javascript执行环境。当回调执行完毕,就退出Javascript执行环境,然后刷新视图。Angular在此基础上创建了一个自己的执行环境及事件处理循环,只有在AngularJS执行环境中运行的操作,才能享受到AngularJS提供的数据绑定,异常处理,资源管理等功能和服务。Angular和浏览器事件循环交互如下:



下面用个例子来解析一下angular执行过程:
示例代码一:html



示例代码一:controller



步骤解析:

上面的“点击”按钮绑定到angular的点击事件,当用户点击按钮时,angular会把changeData函数包装并传入到$scope.$apply(),通过调用$scope.$apply(changeData),进入到angular执行环境,在angular环境中执行changeData。

AngularJS进入$digest循环。这个循环是由两个小循环组成的: $evalAsync队列和$watch列表。执行changeData,changeData中修改了$scope.data;同时,angular遍历整个$watch列表,检测到$watch 列表中的data值的变化,然后再次启动一轮$digest 循环;

直到检测到$watch 列表不再有任何变化后,AngularJS的$digest循环结束,离开AngularJS和Javascript的执行环境。

浏览器把改变的数据data进行重渲染。

$watch
当我们在UI元素中绑定一个$scope对象时,就会往$watch list里面添加一个$watch,对于所有绑定给同一$scope对象的UI元素,也只会添加一个$watch到$watch列表中。
看如下代码示例代码一:html



解析:这里有两个一样的$scope.name,还有个$scope.pass,所以在$watch list里面加入两个$watch。

示例代码二:html



示例代码二:controller



解析:这里虽然在controller中定义了两个$scope对象,但是只有一个$scope.a是绑定到UI元素中的,所以只在$watch list里面加入一个$watch。

$digest&
当浏览器接收到可以被angular 执行环境处理的事件时(比如ng-click、ng-keypress),$digest循环就会触发。这个循环是由两个小的循环组合起来的。一个处理evalAsync队列,另一个处理$watch队列。
在$digest循环中,angular会遍历完整个$watch列表,所有的$watch都检查完后只要有任何一个$watch的值发生变化,这个循环就会再次触发,继续遍历$watch列表直到检测到不再有任何变化。为什么要再次运行这一循环?因为如果你更新了$watch列表中某个用于更新另一个值的值,Angular将检测不到更新,除非再次运行这个循环。看如下
例子示例代码一:html



示例代码一:controller



解析:当我们点击按钮的时候,改变了num1的数据,由于num2数据是受num1数据影响的,如果不再次启动$digest循环,Angular将检测不到num2数据的更新。这就是所谓的脏值检测。这样就能够保证每个model都已经不会再变化。记住如果循环超过10次的话,它将会抛出一个异常,防止无限循环。 当$digest循环结束时,DOM相应地变化。

$apply
$apply()函数可以使事件进入到Angular的执行环境中执行。那么问题来了,什么时候该用$apply(),什么时候不该用呢?
Angular提供的可用于视图中的任意指令(比如ng-click、ng-keypress)、Angular内置的服务(比如$http、$timeout等),使用的时候都会自动调用$apply()。所以,我们不用再去手动调用$apply()了;
当我们手动处理事件,使用第三方框架(比如jQuery、Facebook API),或者调用setTimeout(),他们并没有调用$apply(),所以事件不能在angular执行环境中执行,$digest循环无法检测到事件中对视图数据的更改,视图无法更新。这个时候,我们就需要手动调用$apply()。

由睿江云研发人员提供,想了解更多,请登陆www.eflycloud.com~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: