Angular 学习笔记 :初识 $digest , $watch , $apply,浅析用法 。

传统的浏览器事件循环 :浏览器本身一直在等待事件,并作出响应。如果你点击一个button或者在input 中输入字符,我们在 JS 中 监听这些事件并设定了回调函数,那么这些事件被触发以后,回调函数就会在浏览器的解释器里执行。执行完毕以后,浏览器就会对相应的 DOM 做出变化。

Angular 的事件循环 :Angular 对传统的循环进行了扩展,生成了一个称为 angular context 的执行环境。

$watch 队列 :

$watch : 你在 controller 中创建数据模型,然后你将它们绑定到 UI 上,同时也会有相应数量的$watch(我觉得队列中的这些$watch 是$watch 这个类的实例对象)被加入到$watch 队列用来监视数据模型的变化。需要注意的是,在 UI 上有多少东西接受了数据绑定,就会生成多少个$watch 插入队列中。这些$watch 是在我们的模板加载完毕的时候,也是就linking 阶段生成的(模板加载之前是compile 阶段)。我们可以用$watch 监视某一数据的变化并设定回调函数来响应事件。

$watch(’ModelName’,function( new Value , oldValue ) { //do sth} );

try it : http://jsbin.com/ucaxan/1/edit

$digest 循环:

当浏览器接收到可以被 angular context 处理的事件的时候,$digest 循环就会被触发 。这个循环是有两个小的循环组合起来的。一个处理 evalAsync队列,另一个处理$watch 队列。

$digest 会依次询问 angular context 中$watch 队列中的每个$watch,这就是所谓的脏值检查(dirty-checking)。‘你的值是多少?你改变过没有?’,直到队列成员都被检查过。一旦有一个值发生过变化,这个循环就会再执行一次,直到所有的$watch 都没有发生过变化。 但是如果连续循环超过10次的话,他将会抛出一个一场,防止无限循环。当$digest 循环结束,DOM 相应地变化。有点蛋疼的是,在这种机制下,即便你在input 输入一个字母,$digest 也会检查整个页面的$watch.

$apply :

既然 angular 是通过$digest 循环来检查页面中的$watch变化情况的,并且我们知道,浏览器只有接收到了 angular context 可以处理的事件时才会启动这个循环。那么是谁决定了哪些事件会进入$digest 循环,哪些不会呢。这就是$apply 。也就是说,$apply 决定了哪些事件会触发脏值检查。很多事件比如ng-click,ng-model,这些元素的事件会进入 angular context。这是 angular 自己帮我们做好的。比如下面的例子

app.directive('clickable', function() {

return {
  restrict: "E",
  scope: {
    foo: '=',
    bar: '='
  },
  template: '<ul ><li>{{foo}}</li><li>{{bar}}</li></ul>', 


link: function(scope, element, attrs) {


    element.bind('click', function() {


      scope.foo++; scope.bar++; });


}


}


});


app.controller('MainCtrl', function($scope) { $scope.foo = 0; $scope.bar = 0; });


这是一个简单的 angular 的 directive 。我们通过 link 函数为它添加了 click 事件。但是当我们点击的时候发现页面并不会进行数据的刷新。

try it :http://jsbin.com/opimat/2/

这是因为我们绑定的click 事件并没有进入 angular context 中。这时候我们就需要通过$apply 来实现。

方法一:修改 link函数:

element.bind('click',function(){

  scope.foo++ ;

  scope.bar++;

  scope.$apply();

});

方法二(推荐):

element.bind('click',function(){

  scope.$apply(function(){

    scope.foo++ ;

    scope.bar++;

  })

});

两种方法的区别在于,第一种我们是在 angular context 外面更新的数据。但是一旦处理数据发生错误,angular 就不会知道发生了什么。