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();
这两种方式虽然都能工作,但是第一个方法更安全,因为它能确保异常被正确捕获并处理。
评论