【问题标题】:Why does AngularJS execute function on every digest loop?为什么 AngularJS 在每个摘要循环上都执行函数?
【发布时间】:2015-06-11 00:54:37
【问题描述】:

我来自 Knockout,我正在尝试了解 Angular 如何更新范围。我有点困惑,为什么在作用域上定义的函数(例如$scope.doStuff = function())会在每次作用域刷新时执行。

Plnkr 链接:http://plnkr.co/edit/YnvOELGkRbHOovAWPIpF?p=preview

例如:

HTML

<div ng-controller="one">
  <input type="text" ng-model="a">
  {{b()}}
</div>

JS

angular.module('test', [])
 .controller('one', ['$scope', function ($scope) {
   $scope.a = 'one';
   $scope.b = function () {
     console.log('b has executed');
   }
}])

因此,每当$scope.a 的输入表单字段中发生事件时,就会执行函数$scope.b。为什么会这样?该函数没有任何依赖关系,总是刷新似乎效率低下。

如果我在相同的结构中添加另一个控制器,如下所示:

HTML

<div ng-controller="one">
  <input type="text" ng-model="a">
  {{b()}}
</div>
<div ng-controller="two">
  <input type="text" ng-model="x">
  {{y()}}
</div>

JS

angular.module('test', [])
.controller('one', ['$scope', function ($scope) {
  $scope.a = 'one';
  $scope.b = function () {
    console.log('b has executed');
  }
}])

.controller('two', ['$scope', function ($scope) {
  $scope.x = 'two';
  $scope.y = function () {
    console.log('y has executed');
  }
}])

每次我在控制器one 中更新$scope.a 时,输出为:

b has executed
y has executed

为什么控制器two 执行$scope.y?我认为创建一个新控制器会创建一个新的子范围。是不是因为子作用域链接到父作用域?

更有趣的是,如果我在控制器 two 中更新 $scope.x,则输出为:

b has executed
y has executed
b has executed <-- a second time... what?

为什么函数$scope.b 会再次执行?

那么为什么 Angular 中的函数会在每次范围刷新时执行?

【问题讨论】:

  • angular 必须确保没有改变视图,所以它会执行你在视图中使用的所有函数,并检查是否所有在视图中使用的变量
  • 所以如果我有一个包含 1000 个项目的ng-repeat,并且每个项目都有一个ng-show="function()",这意味着每次我更新一个文本字段时,Angular 都会执行该函数 1000 次?这在性能方面似乎很糟糕。
  • 所以不要将它与 1000 个项目一起使用 :-)
  • 哦,所以不要使用Angular.JS?好,知道了。 :P
  • 如果您可以提供您尝试的示例,我们可能会帮助您减少项目或函数调用。此外,如果您的一个参数集的函数只能返回一个常量值,您可以使用 memoization保存具体参数的结果函数,如果参数未更改则返回它

标签: javascript angularjs knockout.js scope angularjs-scope


【解决方案1】:

Angular 使用所谓的脏检查。为了维护视图和控制器之间的绑定,任何绑定到函数的变量都必须经过验证。

像您演示的那样使用通常是一个坏主意,并且会影响中型到大型应用程序的性能。

建议使用固定变量绑定到视图并在需要时进行更改,这将提高整体性能,并且仅重新渲染已更改的部分。

一般来说,您不会从视图中“调用”函数,但有时这是在 ng-repeat 中使用动态数据的唯一方法,那么我会将这些数据放入对象/数组并返回该对象/数组,那么即使 Angular 将继续在其摘要周期中调用该函数,如果没有更改,它也不会“更新”视图。

【讨论】:

  • 哦...我明白了!这是有道理的。因此,完成我想要做的另一种方法是在不同的上下文中评估函数并将答案存储在范围内,之后 Angular 将通过脏检查来获取更改。谢谢!
【解决方案2】:

这里我是这么认为的,每次页面加载时,Angular js 都会启动该函数,这意味着每次页面加载时都会执行,所以直接调用,使用 ng-change 调用它。

<div ng-controller="one">
  <input type="text" ng-model="a" ng-change=b()>

 </div>
 <div ng-controller="two">
  <input type="text" ng-model="x" ng-change=y()>
</div>

在控制器中,您可以将该功能分配给您所需的 ng-model,如下所示,

angular.module('test', [])
.controller('one', ['$scope', function ($scope) {

  $scope.b = function () {
 $scope.a = 'one';

console.log('b has executed');
  }
}])

.controller('two', ['$scope', function ($scope) {

 $scope.y = function () {
  $scope.x = 'two';
  console.log('y has executed');
   }
  }]) 

否则,您也可以通过分配给 ng-model 将函数值返回给您的 ng-model,它会给出正确的答案,而不是每次都调用。

【讨论】:

    【解决方案3】:

    仅仅是因为不可能知道函数的所有依赖项是什么。假设你的函数b(在控制器one上)是这样的:

      $scope.b = function () {
        console.log('b has executed');
        another_func($scope);
      }
    

    函数another_func的定义是这样的:

    function another_func (obj) { 
        obj.a = 'something';
        return obj.a;
    }
    

    您如何以编程方式知道函数 $scope.b 将调用一个函数,该函数将调用另一个函数以获取依赖于 $scope.a 的值?

    【讨论】:

    • 有道理,感谢您的解释。也许这超出了这个问题的范围,但是有没有办法限制函数何时执行?在 Knockout 中,您可以仅在函数中的依赖项发生更改时触发函数执行,在 Angular 中是否有类似的方法来实现相同的事情?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多