Verificando sujo o $scope
objeto
Angular mantém um simples array
observador nos $scope
objetos. Se você inspecionar algum, $scope
verá que ele contém um array
chamado $$watchers
.
Cada observador é um object
que contém, entre outras coisas
- Uma expressão que o observador está monitorando. Pode ser apenas um
attribute
nome ou algo mais complicado.
- Um último valor conhecido da expressão. Isso pode ser verificado com relação ao valor atual calculado da expressão. Se os valores diferirem, o inspetor acionará a função e marcará
$scope
como suja.
- Uma função que será executada se o inspetor estiver sujo.
Como os observadores são definidos
Existem muitas maneiras diferentes de definir um observador no AngularJS.
Você pode explicitamente $watch
um attribute
no $scope
.
$scope.$watch('person.username', validateUnique);
Você pode colocar uma {{}}
interpolação no seu modelo (um observador será criado para você no atual $scope
).
<p>username: {{person.username}}</p>
Você pode solicitar uma diretiva ng-model
para definir o inspetor para você.
<input ng-model="person.username" />
O $digest
ciclo verifica todos os observadores em relação ao seu último valor
Quando interagimos com o AngularJS pelos canais normais (ng-model, ng-repeat, etc.), um ciclo de resumo será acionado pela diretiva.
Um ciclo de resumo é uma travessia profunda de $scope
todos os seus filhos . Para cada um $scope
object
, iteramos sobre ela $$watchers
array
e avaliamos todas as expressões. Se o novo valor da expressão for diferente do último valor conhecido, a função do observador será chamada. Essa função pode recompilar parte do DOM, recalcular um valor $scope
, acionar um AJAX
request
, qualquer coisa que você precise fazer.
Todo escopo é percorrido e toda expressão de observação é avaliada e verificada em relação ao último valor.
Se um observador for acionado, ele $scope
estará sujo
Se um observador for acionado, o aplicativo saberá que algo mudou e $scope
está marcado como sujo.
As funções do observador podem alterar outros atributos nos $scope
pais $scope
. Se uma $watcher
função foi acionada, não podemos garantir que nossas outras $scope
s ainda estejam limpas e, portanto, executamos todo o ciclo de digestão novamente.
Isso ocorre porque o AngularJS possui ligação bidirecional, para que os dados possam ser transmitidos de volta para a $scope
árvore. Podemos alterar um valor para um valor mais alto $scope
que já foi digerido. Talvez nós alteremos um valor no $rootScope
.
Se $digest
estiver sujo, executamos o $digest
ciclo inteiro novamente
Percorremos o $digest
ciclo continuamente até que o ciclo de digestão seja limpo (todas as $watch
expressões têm o mesmo valor que tinham no ciclo anterior) ou atingimos o limite de digestão. Por padrão, esse limite é definido em 10.
Se atingirmos o limite de resumo, o AngularJS gerará um erro no console:
10 $digest() iterations reached. Aborting!
O resumo é difícil para a máquina, mas fácil para o desenvolvedor
Como você pode ver, sempre que algo muda em um aplicativo AngularJS, o AngularJS verifica todos os observadores da $scope
hierarquia para ver como responder. Para um desenvolvedor, este é um benefício enorme da produtividade, como agora você precisa escrever quase nenhum código de fiação, o AngularJS notará apenas se um valor foi alterado e tornará o restante do aplicativo consistente com a alteração.
Do ponto de vista da máquina, embora isso seja extremamente ineficiente e reduzirá a velocidade do aplicativo, se criarmos muitos observadores. Misko citou uma cifra de cerca de 4000 observadores antes que seu aplicativo pareça lento em navegadores mais antigos.
É fácil atingir esse limite se você ng-repeat
exceder um valor grande, JSON
array
por exemplo. Você pode atenuar isso usando recursos como ligação única para compilar um modelo sem criar observadores.
Como evitar a criação de muitos observadores
Sempre que seu usuário interage com seu aplicativo, cada observador em seu aplicativo será avaliado pelo menos uma vez. Uma grande parte da otimização de um aplicativo AngularJS é reduzir o número de observadores em sua $scope
árvore. Uma maneira fácil de fazer isso é com uma ligação única .
Se você possui dados que raramente mudam, é possível vinculá-los apenas uma vez usando a sintaxe ::, assim:
<p>{{::person.username}}</p>
ou
<p ng-bind="::person.username"></p>
A ligação será acionada apenas quando o modelo contido for renderizado e os dados carregados $scope
.
Isso é especialmente importante quando você tem um ng-repeat
item com muitos itens.
<div ng-repeat="person in people track by username">
{{::person.username}}
</div>