AngularJS 中的 $apply 和 $digest

jonathan
2016-07-05 / 0 评论

AngularJS 中的 $apply 和 $digest

避免在控制器中使用 $watch

永远不要在控制器中使用 $watch,因为它会使控制器难以测试。Angular 提供了 $watchCollection 方法来对对象属性或数组元素设置浅监控,只要属性发生变化就会触发监听器回调。

$watchCollection 示例

$watchCollection() 函数也返回一个注销函数。调用这个注销函数时,也会取消集合上的监听。

手动调用 $apply()

如果你使用了 JavaScript 中的 setTimeout() 来更新一个 scope model,那么 AngularJS 就没有办法知道你更改了什么。这种情况下,调用 $apply() 是你的责任,通过调用它来触发一轮 $digest 循环。

示例代码

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();
});

这段代码会在控制台显示更新后的 model,但视图不会更新。

使用 $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();
});

现在视图会在两秒钟后更新。

注意: 应该使用 $timeout service 而不是 setTimeout(),因为前者会自动调用 $apply()。

$digest 循环

当 $digest 循环运行时,watchers 会被执行来检查 scope 中的 models 是否发生了变化。如果发生了变化,相应的 listener 函数就会被执行。为了处理 listener 函数可能修改 scope model 的情况,AngularJS 实现了脏检查机制(Dirty Checking)。这意味着 $digest 循环会持续运行直到 models 不再发生变化,或者循环次数达到10次。

结语

总是将要执行的代码和函数传递给 $apply 去执行。 使用 $apply 时,应该像下面这样:

$scope.$apply(function() {
  $scope.variable1 = 'some value';
  executeSomeAction();
});

而不是这样:

$scope.variable1 = 'some value';
executeSomeAction();
$scope.$apply();

这两种方式虽然都能工作,但是第一个方法更安全,因为它能确保异常被正确捕获并处理。

评论

博主关闭了当前页面的评论