码迷,mamicode.com
首页 > 移动开发 > 详细

浅析$watch ,$apply 和 $digest (Angular篇)

时间:2017-12-03 20:53:04      阅读:224      评论:0      收藏:0      [点我收藏+]

标签:images   队列   检查   也会   交互   put   facebook   不能   img   

 

前言

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

浏览器事件和angular扩展

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

 技术分享图片

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

html

<button ng-click="changeData()">点击</button>
<p>{{data}}</p>

 

controller

$scope.changeData=function(){
    $scope.data=1;
}

 

(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列表中。看如下代码

(1)

html

<input ng-model="name" type="text" placeholder="Your name">
<input ng-model="age"  type="text" placeholder="Your age">
<h1>Hello {{name}}</h1>

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

(2)

html

<p>{{a}}</p>

 

controller

$scope.a=‘dataA‘;
$scope.a=‘dataB‘;

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

<button ng-click="changeNum()">点击</button>
<p>{{num1}}</p>
<p>{{num2}}</p>

 

controller

$scope.num1=1;
$scope.num2=$scope.num1+2;
$scope.changeNum=function(){
    $scope.num1=2;
}

当我们点击按钮的时候,改变了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();

 

浅析$watch ,$apply 和 $digest (Angular篇)

标签:images   队列   检查   也会   交互   put   facebook   不能   img   

原文地址:http://www.cnblogs.com/huiguo/p/7967075.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!