Como a ligação de dados funciona na AngularJS
estrutura?
Não encontrei detalhes técnicos em seu site . É mais ou menos claro como ele funciona quando os dados são propagados da visualização para o modelo. Mas como o AngularJS rastreia alterações nas propriedades do modelo sem setters e getters?
Descobri que existem observadores de JavaScript que podem fazer esse trabalho. Mas eles não são suportados no Internet Explorer 6 e Internet Explorer 7 . Então, como o AngularJS sabe que eu mudei, por exemplo, o seguinte e refleti essa alteração em uma exibição?
myobject.myproperty="new value";
javascript
angularjs
data-binding
Pashec
fonte
fonte
Respostas:
AngularJS lembra o valor e o compara com um valor anterior. Isso é checagem suja básica. Se houver uma alteração no valor, ele dispara o evento de alteração.
O
$apply()
método, que é o que você chama quando está migrando de um mundo não AngularJS para um mundo AngularJS, é chamado$digest()
. Um resumo é simplesmente uma checagem suja e antiga. Ele funciona em todos os navegadores e é totalmente previsível.Para comparar a verificação suja (AngularJS) x os ouvintes de alteração ( KnockoutJS e Backbone.js ): Embora a verificação suja possa parecer simples e até ineficiente (abordarei isso mais tarde), verifica-se que ela é semanticamente correta o tempo todo, enquanto os ouvintes de mudança têm muitos casos de canto estranhos e precisam de coisas como rastreamento de dependência para torná-lo mais semanticamente correto. O rastreamento de dependência do KnockoutJS é um recurso inteligente para um problema que o AngularJS não possui.
Problemas com ouvintes de mudança:
E o desempenho?
Portanto, pode parecer que somos lentos, pois a verificação suja é ineficiente. É aqui que precisamos olhar para números reais em vez de apenas argumentos teóricos, mas primeiro vamos definir algumas restrições.
Os seres humanos são:
Lento - Qualquer coisa acima de 50 ms é imperceptível aos seres humanos e, portanto, pode ser considerado "instantâneo".
Limitado - você não pode realmente mostrar mais de 2000 informações a um humano em uma única página. Qualquer coisa além disso é uma interface do usuário realmente ruim, e os humanos não podem processar isso de qualquer maneira.
Portanto, a verdadeira questão é a seguinte: quantas comparações você pode fazer em um navegador em 50 ms? Essa é uma pergunta difícil de responder ao maior número de fatores, mas aqui está um caso de teste: http://jsperf.com/angularjs-digest/6, que cria 10.000 observadores. Em um navegador moderno, isso leva pouco menos de 6 ms. No Internet Explorer 8, são necessários cerca de 40 ms. Como você pode ver, isso não é um problema, mesmo em navegadores lentos atualmente. Há uma ressalva: as comparações precisam ser simples para se ajustarem ao limite de tempo ... Infelizmente, é muito fácil adicionar uma comparação lenta no AngularJS; portanto, é fácil criar aplicativos lentos quando você não sabe o que deseja. estão fazendo. Mas esperamos ter uma resposta, fornecendo um módulo de instrumentação, que mostre quais são as comparações lentas.
Acontece que os videogames e GPUs usam a abordagem de verificação suja, especificamente porque é consistente. Contanto que eles superem a taxa de atualização do monitor (normalmente de 50 a 60 Hz ou a cada 16,6 a 20 ms), qualquer desempenho excedente é um desperdício; portanto, é melhor desenhar mais coisas do que aumentar o FPS.
fonte
Misko já deu uma excelente descrição de como as ligações de dados funcionam, mas eu gostaria de adicionar minha visão sobre o problema de desempenho à ligação de dados.
Como afirmou Misko, por volta de 2000 ligações é onde você começa a ver problemas, mas você não deve ter mais de 2000 informações em uma página. Isso pode ser verdade, mas nem todas as ligações de dados são visíveis para o usuário. Depois de começar a criar qualquer tipo de widget ou grade de dados com ligação bidirecional, é possível acessar facilmente as ligações com 2000, sem ter um UX ruim.
Considere, por exemplo, uma caixa de combinação na qual você pode digitar texto para filtrar as opções disponíveis. Esse tipo de controle pode ter ~ 150 itens e ainda ser altamente utilizável. Se houver algum recurso extra (por exemplo, uma classe específica na opção atualmente selecionada), você começará a receber 3-5 ligações por opção. Coloque três desses widgets em uma página (por exemplo, um para selecionar um país, o outro para selecionar uma cidade no país em questão e o terceiro para selecionar um hotel) e você já deve ter entre 1000 e 2000 ligações.
Ou considere uma grade de dados em um aplicativo Web corporativo. 50 linhas por página não são irracionais, cada uma delas com 10 a 20 colunas. Se você criar isso com ng-repetições e / ou tiver informações em algumas células que usam algumas ligações, poderá aproximar-se de 2000 ligações apenas com essa grade.
Acho que esse é um problema enorme ao trabalhar com o AngularJS, e a única solução que consegui encontrar até agora é construir widgets sem usar a ligação bidirecional, em vez de usar o ngOnce, cancelando o registro de observadores e truques semelhantes ou construindo diretivas que constroem o DOM com jQuery e manipulação de DOM. Eu sinto que isso derrota o propósito de usar Angular em primeiro lugar.
Eu adoraria ouvir sugestões de outras maneiras de lidar com isso, mas talvez eu deva escrever minha própria pergunta. Eu queria colocar isso em um comentário, mas acabou sendo muito longo para isso ...
TL; DR
A ligação de dados pode causar problemas de desempenho em páginas complexas.
fonte
Verificando sujo o
$scope
objetoAngular mantém um simples
array
observador nos$scope
objetos. Se você inspecionar algum,$scope
verá que ele contém umarray
chamado$$watchers
.Cada observador é um
object
que contém, entre outras coisasattribute
nome ou algo mais complicado.$scope
como suja.Como os observadores são definidos
Existem muitas maneiras diferentes de definir um observador no AngularJS.
Você pode explicitamente
$watch
umattribute
no$scope
.Você pode colocar uma
{{}}
interpolação no seu modelo (um observador será criado para você no atual$scope
).Você pode solicitar uma diretiva
ng-model
para definir o inspetor para você.O
$digest
ciclo verifica todos os observadores em relação ao seu último valorQuando 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 umAJAX
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á sujoSe 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 novamentePercorremos 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:
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:
ou
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.fonte
Este é o meu entendimento básico. Pode muito bem estar errado!
$watch
método.$apply
método.$apply
o$digest
método é invocado que passa por cada um dos relógios e verifica se eles mudaram desde a última vez que o$digest
ran.No desenvolvimento normal, a sintaxe de ligação de dados no HTML diz ao compilador AngularJS para criar os relógios para você e os métodos do controlador
$apply
já são executados dentro dele . Portanto, para o desenvolvedor do aplicativo, tudo é transparente.fonte
Eu me perguntei isso por um tempo. Sem setters, como o
AngularJS
aviso muda para o$scope
objeto? Ele os pesquisa?Na verdade, o que ele faz é o seguinte: qualquer local "normal" para o qual você modifica o modelo já foi chamado a partir das tripas
AngularJS
, portanto ele o chama automaticamente$apply
após a execução do código. Digamos que seu controlador tenha um método conectado ang-click
algum elemento. ComoAngularJS
liga a chamada desse método para você, ele tem a chance de fazer um$apply
no local apropriado. Da mesma forma, para expressões que aparecem diretamente nas visualizações, essas são executadas porAngularJS
isso$apply
.Quando a documentação fala sobre precisar chamar
$apply
manualmente o código foraAngularJS
dele, está falando sobre o código que, quando executado, não se originaAngularJS
na pilha de chamadas.fonte
Explicando com imagens:
A vinculação de dados precisa de um mapeamento
A referência no escopo não é exatamente a referência no modelo. Quando você vincula dois objetos a dados, precisa de um terceiro que escute o primeiro e modifique o outro.
Aqui, quando você modifica o
<input>
, você toca em data-ref3 . E o mecanismo clássico de ligação de dados mudará data-ref4 . Então, como as outras{{data}}
expressões se moverão?Eventos levam a $ digest ()
Angular mantém um
oldValue
enewValue
de cada ligação. E após cada evento Angular , o famoso$digest()
loop verifica a WatchList para ver se algo mudou. Estes eventos angulares sãong-click
,ng-change
,$http
concluída ... O$digest()
circuito vontade, desde que nenhumoldValue
difere donewValue
.Na figura anterior, ele notará que data-ref1 e data-ref2 foram alterados.
Conclusões
É um pouco como o ovo e a galinha. Você nunca sabe quem começa, mas espero que funcione na maioria das vezes como esperado.
O outro ponto é que você pode entender facilmente o impacto profundo de uma ligação simples na memória e na CPU. Espero que os desktops sejam gordos o suficiente para lidar com isso. Os telefones celulares não são tão fortes.
fonte
Obviamente, não há verificação periódica
Scope
se há alguma alteração nos objetos anexados a ele. Nem todos os objetos anexados ao escopo são observados. O escopo mantém prototipicamente um observador $$ .Scope
somente itera$$watchers
quando$digest
é chamado.Angular adiciona um observador aos observadores $$ para cada um desses
A função $ watch possui três parâmetros:
Existe uma coisa interessante no Angular chamada Digest Cycle. O ciclo $ digest começa como resultado de uma chamada para $ scope. $ Digest (). Suponha que você altere um modelo $ scope em uma função de manipulador por meio da diretiva ng-click. Nesse caso, o AngularJS aciona automaticamente um ciclo $ digest chamando $ digest (). Além do ng-click, existem várias outras diretivas / serviços integrados que permitem alterar modelos (por exemplo, ng-model, $ timeout, etc.) e acionar automaticamente um ciclo de digestão $. A implementação grosseira do $ digest é assim.
Se usarmos a função setTimeout () do JavaScript para atualizar um modelo de escopo, o Angular não tem como saber o que você pode alterar. Nesse caso, é nossa responsabilidade chamar $ apply () manualmente, o que aciona um ciclo de digestão $. Da mesma forma, se você tiver uma diretiva que configure um ouvinte de eventos DOM e altere alguns modelos dentro da função manipuladora, precisará chamar $ apply () para garantir que as alterações entrem em vigor. A grande idéia do $ apply é que podemos executar algum código que não esteja ciente do Angular, que ainda pode mudar as coisas no escopo. Se envolvermos esse código em $ apply, ele chamará $ digest (). Implementação aproximada de $ apply ().
fonte
O AngularJS lida com o mecanismo de ligação de dados com a ajuda de três funções poderosas: $ watch () , $ digest () e $ apply () . Na maioria das vezes, o AngularJS chama os $ scope. $ Watch () e $ scope. $ Digest (), mas, em alguns casos, pode ser necessário chamar essas funções manualmente para atualizar com novos valores.
$ watch () : -
$ digest () -
$ apply () -
fonte
Aconteceu que eu precisava vincular um modelo de dados de uma pessoa a um formulário, o que fiz foi um mapeamento direto dos dados com o formulário.
Por exemplo, se o modelo tivesse algo como:
A entrada de controle do formulário:
Dessa forma, se você modificar o valor do controlador de objeto, isso será refletido automaticamente na visualização.
Um exemplo em que passei o modelo é atualizado a partir dos dados do servidor é quando você solicita um CEP e um CEP com base em cargas escritas carrega uma lista de colônias e cidades associadas a essa exibição e, por padrão, define o primeiro valor com o usuário. E isso funcionou muito bem, o que acontece é que,
angularJS
às vezes, leva alguns segundos para atualizar o modelo; para fazer isso, você pode colocar um botão giratório enquanto exibe os dados.fonte
A ligação de dados unidirecional é uma abordagem em que um valor é obtido do modelo de dados e inserido em um elemento HTML. Não há como atualizar o modelo da visualização. É usado em sistemas de gabaritos clássicos. Esses sistemas vinculam dados em apenas uma direção.
A ligação de dados em aplicativos Angular é a sincronização automática de dados entre o modelo e os componentes de exibição.
A ligação de dados permite tratar o modelo como a única fonte de verdade em seu aplicativo. A vista é uma projeção do modelo em todos os momentos. Se o modelo for alterado, a visualização refletirá a mudança e vice-versa.
fonte
Aqui está um exemplo de ligação de dados com o AngularJS, usando um campo de entrada. Vou explicar mais tarde
Código HTML
Código AngularJS
Como você pode ver no exemplo acima, o AngularJS usa
ng-model
para ouvir e assistir o que acontece nos elementos HTML, especialmente nosinput
campos. Quando algo acontecer, faça alguma coisa. No nosso caso,ng-model
está vinculado à nossa visão, usando a notação bigode{{}}
. Tudo o que é digitado dentro do campo de entrada é exibido na tela instantaneamente. E essa é a beleza da ligação de dados, usando o AngularJS em sua forma mais simples.Espero que isto ajude.
Veja um exemplo de trabalho aqui no Codepen
fonte
AngularJs suporta ligação de dados bidirecional .
Significa que você pode acessar os dados Ver -> Controlador e Controlador -> Ver
Para Ex.
1)
O / P
Você pode ligar dados no
ng-model
Like: -2)
Aqui no exemplo acima, qualquer que seja o usuário de entrada, será visível na
<div>
tag.Se desejar vincular a entrada do html ao controlador: -
3)
Aqui, se você deseja usar a entrada
name
no controlador,ng-model
vincula nossa visão e a expressa{{ }}
.ng-model
são os dados mostrados ao usuário na visualização e com os quais o usuário interage.Portanto, é fácil vincular dados em AngularJs.
fonte
O Angular.js cria um observador para cada modelo que criamos em exibição. Sempre que um modelo é alterado, uma classe "ng-dirty" é anexada ao modelo, para que o observador observe todos os modelos com a classe "ng-dirty" e atualize seus valores no controlador e vice-versa.
fonte
ligação de dados:
O que é ligação de dados?
Sempre que o usuário altera os dados na visualização, ocorre uma atualização dessa alteração no modelo de escopo e vice-versa.
Como isso é possível?
Resposta curta: Com a ajuda do ciclo de digestão.
Descrição: js angulares define o inspetor no modelo de escopo, que aciona a função de ouvinte se houver uma alteração no modelo.
// Código de atualização do Dom com novo valor
});
Então, quando e como é chamada a função de observador?
A função de observador é chamada como parte do ciclo de digestão.
O ciclo de resumo é chamado automaticamente acionado como parte de js angulares construídos em diretivas / serviços como ng-model, ng-bind, $ timeout, ng-click e outros .. que permitem ativar o ciclo de resumo.
Função do ciclo digestivo:
ie
$rootScope.$apply()
Nota: $ apply () é igual a $ rootScope. $ Digest () significa que a verificação suja começa diretamente da raiz ou do topo ou do escopo pai até todos os escopos filho $ no aplicativo js angular.
Os recursos acima também funcionam nos navegadores IE para as versões mencionadas, apenas certificando-se de que seu aplicativo seja um aplicativo js angular, o que significa que você está usando o arquivo de script de estrutura angularjs mencionado na tag de script.
Obrigado.
fonte