Как остановить цикл дайджеста вручную в angularjs
Как цикл дайджест выполняет грязную проверку переменной, которая есть, если имеется 100 переменных области видимости, и если я изменяю одну переменную, тогда она будет запускать просмотр всех переменных.
Предположим, что у меня есть 100 переменных модели области видимости, которые не зависят друг от друга. Если я вношу изменения в одну переменную, то я не хочу проверять все остальные 99 переменных. Есть какой-либо способ сделать это? Если да, то как?
Ответы
Ответ 1
Удивительно, но это обычно не проблема, у браузеров нет проблем даже с тысячами привязок, если выражения не сложны. Общий ответ для how many watchers are ok to have
: 2000
.
Решения:
Это довольно легко от AngularJS 1.3
, так как привязки one-time
теперь находятся в ядре.
- Один раз привязка переменных.
Мы можем использовать директиву (::)
, привязавшуюся к одному времени, чтобы предотвратить наблюдение за нежелательными переменными. Здесь переменная будет смотреть только один раз и после этого не будет обновлять эту переменную.
- Остановить цикл дайджеста вручную.
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 будет допрашивать каждого наблюдателя за изменениями.
для stop
и pause
дайджест:
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
Спасибо.
Ответ 2
Это хороший вопрос и подчеркивает один из самых больших недостатков Angular 1.x. Существует мало контроля над тем, как управляется цикл дайджест. Он предназначен для использования в черном ящике и для более крупных приложений, что может привести к серьезным проблемам с производительностью. Нет способа Angular делать то, что вы предлагаете, но есть что-то, что поможет вам достичь одних и тех же целей (т.е. Улучшить производительность цикла дайджеста, когда изменяется только одна вещь).
Я рекомендую использовать подключаемый модуль-уведомление. У меня нет никаких отношений с проектом, но я использую его для своего проекта и имел большой успех с ним.
Идея заключается в том, что вы можете указать определенные привязки только для $digested, когда определенное событие было поднято.
Существует несколько способов использования плагина, но вот тот, который я считаю эффективным:
В файле шаблона укажите привязку с использованием специального синтаксиса привязки-уведомления:
<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
будут проверяться только в широковещательной передаче.
Несколько вещей, которые нужно иметь в виду:
- Это существенно снижает одно из ключевых преимуществ Angular (также это основная слабость для больших приложений). Двусторонняя привязка обычно означает, что вам не нужно активно управлять изменениями в вашей модели, но при этом вы это делаете. Поэтому я бы рекомендовал использовать это для частей вашего приложения, которые имеют множество привязок и вызывают замедление.
-
$emit
и $broadcast
могут влиять на производительность, поэтому попробуйте вызвать их только на небольших частях дерева $scope
(scope
с небольшим количеством или без детей).
- Взгляните на документацию, так как есть несколько способов использования плагина. Выберите шаблон использования, который наилучшим образом подходит для вашего приложения.
Ответ 3
Это довольно конкретный случай использования для выполнения эксклюзивных/условных проверок в цикле дайджеста, и я не думаю, что это возможно без форкирования/взлома ядра angular.
Я бы рассмотрел рефакторинг как/что вы $watching
. Возможно, использование ngModelController
$viewChangeListeners
было бы более подходящим, чем $watch
?