Путем грязной проверки $scope
объекта
Angular поддерживает простой array
наблюдателей в $scope
объектах. Если вы проверите любой из них, $scope
вы обнаружите, что он содержит array
вызываемый $$watchers
.
Каждый наблюдатель - это object
то, что содержит среди прочего
- Выражение, за которым следит наблюдатель. Это может быть просто
attribute
имя или что-то более сложное.
- Последнее известное значение выражения. Это можно проверить по текущему вычисленному значению выражения. Если значения отличаются, наблюдатель запустит функцию и пометит
$scope
как грязную.
- Функция, которая будет выполнена, если наблюдатель загрязнен.
Как определяются наблюдатели
Есть много разных способов определения наблюдателя в AngularJS.
Вы можете явно на .$watch
attribute
$scope
$scope.$watch('person.username', validateUnique);
Вы можете разместить {{}}
интерполяцию в своем шаблоне (для вас будет создан наблюдатель на текущем $scope
).
<p>username: {{person.username}}</p>
Вы можете задать директиву, например, ng-model
определить для вас наблюдателя.
<input ng-model="person.username" />
$digest
Цикл проверяет все наблюдатель против последнего значения
Когда мы взаимодействуем с AngularJS по обычным каналам (ng-модель, ng-repeat и т. Д.), Директива запускает цикл дайджеста.
Цикл дайджеста - это первый шаг$scope
в глубину и всех его потомков . Для каждого $scope
object
мы перебираем его $$watchers
array
и оцениваем все выражения. Если новое значение выражения отличается от последнего известного значения, вызывается функция наблюдателя. Эта функция может перекомпилировать часть DOM, повторно вычислить значение $scope
, вызвать AJAX
request
, все, что вам нужно сделать.
Каждая область видимости прослеживается, и каждое выражение наблюдения оценивается и проверяется по последнему значению.
Если наблюдатель срабатывает, $scope
значит он грязный
Если сработает наблюдатель, приложение узнает, что что-то изменилось, и $scope
помечено как грязное.
Функции наблюдателя могут изменять другие атрибуты на $scope
или на родительском элементе $scope
. Если одна $watcher
функция была запущена, мы не можем гарантировать, что наши другие $scope
все еще чисты, и поэтому мы снова выполняем весь цикл дайджеста.
Это связано с тем, что AngularJS имеет двустороннюю привязку, поэтому данные могут быть переданы обратно в $scope
дерево. Мы можем изменить значение на более высокое $scope
, которое уже было переварено. Возможно, мы изменим значение на $rootScope
.
Если $digest
грязный, мы $digest
снова выполняем весь цикл
Мы непрерывно перебираем $digest
цикл, пока либо цикл дайджеста не станет чистым (все $watch
выражения имеют то же значение, что и в предыдущем цикле), либо мы не достигнем предела дайджеста. По умолчанию этот предел установлен на 10.
Если мы достигнем предела дайджеста, AngularJS выдаст ошибку в консоли:
10 $digest() iterations reached. Aborting!
Дайджест сложен для машины, но легок для разработчика
Как вы можете видеть, каждый раз, когда что-то меняется в приложении AngularJS, AngularJS проверяет каждого наблюдателя в $scope
иерархии, чтобы увидеть, как реагировать. Для разработчика это огромный выигрыш в производительности, поскольку теперь вам практически не нужно писать код проводки, AngularJS просто заметит, изменилось ли значение, и сделает остальное приложение совместимым с этим изменением.
С точки зрения машины, хотя это крайне неэффективно и замедлит работу нашего приложения, если мы создадим слишком много наблюдателей. Миско привел цифру в 4000 зрителей, прежде чем ваше приложение будет работать медленнее в старых браузерах.
Этот предел легко достичь, если вы, например, ng-repeat
более чем большой JSON
array
. Вы можете избежать этого, используя такие функции, как одноразовая привязка для компиляции шаблона без создания наблюдателей.
Как избежать создания слишком большого количества наблюдателей
Каждый раз, когда ваш пользователь взаимодействует с вашим приложением, каждый наблюдатель в вашем приложении будет оцениваться как минимум один раз. Большая часть оптимизации приложения AngularJS - это уменьшение количества наблюдателей в вашем $scope
дереве. Один из простых способов сделать это - одноразовая привязка .
Если у вас есть данные, которые будут редко меняться, вы можете связать их только один раз, используя синтаксис ::, например:
<p>{{::person.username}}</p>
или
<p ng-bind="::person.username"></p>
Привязка будет срабатывать только тогда, когда содержащий шаблон визуализируется и данные загружаются в $scope
.
Это особенно важно, когда у вас есть ng-repeat
много предметов.
<div ng-repeat="person in people track by username">
{{::person.username}}
</div>