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

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

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

下面用个例子来解析一下angular执行过程: 示例代码一:html https://i-blog.csdnimg.cn/blog_migrate/90b9a55bfb367eaf7b9865cda5cc4787.gif

示例代码一:controller https://i-blog.csdnimg.cn/blog_migrate/769f790f84aae8ca7730e5112a409801.gif

步骤解析:

  1. 上面的“点击”按钮绑定到angular的点击事件,当用户点击按钮时,angular会把changeData函数包装并传入到$scope.$apply(),通过调用$scope.$apply(changeData),进入到angular执行环境,在angular环境中执行changeData。
  2. AngularJS进入$digest循环。这个循环是由两个小循环组成的: $evalAsync队列和$watch列表。执行changeData,changeData中修改了$scope.data;同时,angular遍历整个$watch列表,检测到$watch 列表中的data值的变化,然后再次启动一轮$digest 循环;
  3. 直到检测到$watch 列表不再有任何变化后,AngularJS的$digest循环结束,离开AngularJS和Javascript的执行环境。
  4. 浏览器把改变的数据data进行重渲染。

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

https://i-blog.csdnimg.cn/blog_migrate/3f89bc13274415421c46626f1501c43b.gif

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

示例代码二:html

https://i-blog.csdnimg.cn/blog_migrate/e1a9f8b06e7a05f4c8da829fd56f3d5e.gif

示例代码二:controller

https://i-blog.csdnimg.cn/blog_migrate/e1a9f8b06e7a05f4c8da829fd56f3d5e.gif

解析:这里虽然在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

https://i-blog.csdnimg.cn/blog_migrate/4209b0fda607f55a76fbc32c03b3f059.gif

示例代码一:controller

https://i-blog.csdnimg.cn/blog_migrate/1cbabab9d84ed01d6d49137439e20a81.gif

解析:当我们点击按钮的时候,改变了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~

转载于:https://my.oschina.net/u/3363053/blog/871774

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值