如何在AngularJS中手动停止循环更新(digest cycle)

17

digest周期会对变量进行脏检查,如果有100个作用域变量,并且我改变了其中一个变量,那么将运行所有变量的监视器。

假设我有100个相互独立的作用域模型变量。如果我更改其中一个变量,则不想检查其他99个变量。有没有方法可以实现这一点?如果有,怎么做?


因此,没有这样的方法,但显然您可以使用bindonce指令来减少页面上的观察者,例如{{::myVar}},它只会观察值一次,之后就不会更新值。 - Pankaj Parkar
1
我不能使用一次性绑定。这将从变量中删除监视,并且我需要双向绑定,因为我正在使用 ng-model 变量用于输入框。 - Sunil Garg
也许尝试使用$watchCollection - malix
https://dev59.com/p2Ei5IYBdhLWcg3wltEl - Jorawar Singh
3个回答

34

令人惊讶的是,这通常不是问题,除非表达式很复杂,浏览器即使有成千上万个绑定也不会出现问题。关于 有多少个watchers可以接受 的常见答案是2000

解决方案:

AngularJS 1.3开始很容易,因为一次性绑定现在已经是核心功能。

  1. 变量的一次性绑定。

我们可以使用一次性绑定指令(::)来防止监视器观察不需要监视的变量。在这里,变量只会被监视一次,之后它将不再更新该变量。

  1. 手动停止脏检查周期。

HTML:

<ul ng-controller="myCtrl">
  <li ng-repeat="item in Lists">{{lots of bindings}}</li>
</ul>

控制器代码:

app.controller('myCtrl', function ($scope, $element) {
  $element.on('scroll', function () {
    $scope.Lists = getVisibleElements();
    $scope.$digest();
  });
});

$digest 过程中,您只关心 Lists 对象的更改,而不关心单个项的更改。然而,Angular 仍会检查每个 watcher 是否有更改。

指令用于停止和暂停 digest:

app.directive('stopDigest', function () {
  return {
    link: function (scope) {
      var watchers;

      scope.$on('stop', function () {
        watchers = scope.$$watchers;
        scope.$$watchers = [];
      });

      scope.$on('resume', function () {
        if (watchers)
          scope.$$watchers = watchers;
      });
    }
  };
});

现在,控制器代码应该被更改:

<ul ng-controller="listCtrl">
  <li stop-digest ng-repeat="item in visibleList">{{lots of bindings}}</li>
</ul>

app.controller('myCtrl', function ($scope, $element) {
  $element.on('scroll', function () {
    $scope.visibleList = getVisibleElements();

    $scope.$broadcast('stop');
    $scope.$digest();
    $scope.$broadcast('resume');
  });
});

参考文档: https://coderwall.com/p/d_aisq/speeding-up-angularjs-s-digest-loop

谢谢。


我看到了这篇文章,希望能解决我的问题。我遇到了一些问题,其中创建了许多摘要。您能告诉我如何应用您的逻辑并限制摘要循环吗?这是我的文章链接:https://stackoverflow.com/questions/53816868/slow-rendering-of-html-table-when-binding-json-data - user1563677

6
这是一个好问题,突显了 Angular 1.x 的最大缺陷之一。对于如何管理 digest 循环的方式几乎没有控制权。它被设计为一个黑盒子,在大型应用程序中,这可能会导致显着的性能问题。没有“Angular 方式”来实现您所建议的内容,但有一些东西可以帮助您实现相同的目标(即:当只有一个事物发生变化时,提高 digest 循环的性能)。
我建议使用 bind-notifier 插件。我与该项目没有关系,但我正在将其用于自己的项目,并且取得了巨大的成功。
其背后的想法是,您可以指定某些绑定仅在特定事件被触发时才进行 $digest。
使用插件有多种方法,但以下是我认为最有效的方法:
在模板文件中,使用特殊的 bind-notifier 语法指定一个绑定:
<div>{{:user-data-change:user.name}}</div>
<div>{{:job-data-change:job.name}}</div>

这两个绑定在大多数脏检查周期中不会被检查,除非它们被通知。
在您的控制器中,当用户数据发生更改时,请像这样通知绑定:
this.refreshUserData().then(() => {
  $scope.$broadcast('$$rebind::user-data-change');
});

对于job-data-changed等类似情况同样适用。

使用此方法,只会在广播时检查user.name的绑定。

需要注意以下几点:

  1. 这基本上破坏了Angular的一个关键优势(也是大型应用程序的核心弱点)。双向绑定通常意味着您不需要主动管理模型的更改,但是使用此方法后,您需要这样做。因此,我建议仅将其用于您的应用程序中具有大量绑定并导致减慢速度的部分。
  2. $emit$broadcast本身可能会影响性能,因此请尝试仅在$scope树的小部分(具有少量或没有子级的scope)上调用它们。
  3. 仔细阅读文档,因为有多种使用插件的方法。选择最适合您的应用程序的使用模式。

如何将其应用于ng-repeat? - MSS
它将看起来像这样:<div ng-repeat="item in :event-name:items">...</div> - Andrew Eisenberg
我之前在上面留言说我与这个项目无关。但是现在,我已经接手了这个项目。因此,尽管我当时的回答是正确的,但我不想误导任何人。 - Andrew Eisenberg

1
这是一个非常特定的用例,涉及在摘要周期中进行排他/条件检查,我认为如果没有分叉/黑客angular核心是不可能实现的。
我建议重新设计您正在$watching的方式/内容。也许使用ngModelController的$viewChangeListeners比$watch更合适?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接