Путем грязной проверки объекта $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>