Verificando sujo o $scopeobjeto
Angular mantém um simples arrayobservador nos $scopeobjetos. Se você inspecionar algum, $scopeverá que ele contém um arraychamado $$watchers.
Cada observador é um objectque contém, entre outras coisas
- Uma expressão que o observador está monitorando. Pode ser apenas um
attributenome 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á
$scopecomo 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 $watchum attributeno $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-modelpara definir o inspetor para você.
<input ng-model="person.username" />
O $digestciclo 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 $scopetodos os seus filhos . Para cada um $scope object, iteramos sobre ela $$watchers arraye 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 $scopeestará sujo
Se um observador for acionado, o aplicativo saberá que algo mudou e $scopeestá marcado como sujo.
As funções do observador podem alterar outros atributos nos $scopepais $scope. Se uma $watcherfunção foi acionada, não podemos garantir que nossas outras $scopes 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 $scopeque já foi digerido. Talvez nós alteremos um valor no $rootScope.
Se $digestestiver sujo, executamos o $digestciclo inteiro novamente
Percorremos o $digestciclo continuamente até que o ciclo de digestão seja limpo (todas as $watchexpressõ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 $scopehierarquia 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-repeatexceder um valor grande, JSON arraypor 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-repeatitem com muitos itens.
<div ng-repeat="person in people track by username">
{{::person.username}}
</div>