Путем грязной проверки $scopeобъекта
Angular поддерживает простой arrayнаблюдателей в $scopeобъектах. Если вы проверите любой из них, $scopeвы обнаружите, что он содержит arrayвызываемый $$watchers.
Каждый наблюдатель - это objectто, что содержит среди прочего
- Выражение, за которым следит наблюдатель. Это может быть просто
attributeимя или что-то более сложное.
- Последнее известное значение выражения. Это можно проверить по текущему вычисленному значению выражения. Если значения отличаются, наблюдатель запустит функцию и пометит
$scopeкак грязную.
- Функция, которая будет выполнена, если наблюдатель загрязнен.
Как определяются наблюдатели
Есть много разных способов определения наблюдателя в AngularJS.
Вы можете явно на .$watchattribute$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>