Detecção de Mudança Angular e a Estratégia OnPush

Publicados: 2022-03-11

Você começou a usar o Angular para todos os seus projetos favoritos. Você sabe o que o Angular tem a oferecer e como pode aproveitá-lo para criar aplicativos da Web incríveis. Mas há certas coisas sobre o Angular, e conhecê-las pode torná-lo melhor no uso do Angular para seus projetos.

O fluxo de dados está no centro de quase todas as coisas Angular, a detecção de alterações é algo que vale a pena conhecer, pois ajudará você a rastrear bugs com muito mais facilidade e oferecerá a oportunidade de otimizar ainda mais seus aplicativos ao trabalhar com um conjunto de dados complexo.

Detecção de Mudança Angular e a Estratégia OnPush

Neste artigo, você aprenderá como o Angular detecta alterações em suas estruturas de dados e como você pode torná-las imutáveis ​​para aproveitar ao máximo as estratégias de detecção de alterações do Angular.

Detecção de Mudanças em Angular

Quando você altera qualquer um de seus modelos, o Angular detecta as alterações e atualiza imediatamente as visualizações. Esta é a detecção de alterações em Angular. O objetivo desse mecanismo é garantir que as visualizações subjacentes estejam sempre sincronizadas com seus modelos correspondentes. Esse recurso principal do Angular é o que faz a estrutura funcionar e é, em parte, a razão pela qual o Angular é uma boa escolha para o desenvolvimento de aplicativos da web modernos.

Um modelo em Angular pode mudar como resultado de qualquer um dos seguintes cenários:

  • Eventos DOM (clique, passe o mouse sobre, etc.)

  • Solicitações AJAX

  • Temporizadores (setTimer(), setInterval())

Detectores de Mudanças

Todos os aplicativos Angular são compostos por uma árvore hierárquica de componentes. Em tempo de execução, o Angular cria uma classe de detector de mudança separada para cada componente na árvore, que eventualmente forma uma hierarquia de detectores de mudança semelhante à árvore de hierarquia de componentes.

Sempre que a detecção de alterações é acionada, o Angular percorre essa árvore de detectores de alterações para determinar se algum deles relatou alterações.

O ciclo de detecção de alterações é sempre executado uma vez para cada alteração detectada e começa a partir do detector de alteração de raiz e vai até o fim de forma sequencial. Essa escolha de design sequencial é boa porque atualiza o modelo de maneira previsível, pois sabemos que os dados do componente só podem vir de seu pai.

Alterar hierarquia do detector

Os detectores de alterações fornecem uma maneira de acompanhar os estados anteriores e atuais do componente, bem como sua estrutura, a fim de relatar alterações ao Angular.

Se o Angular obtiver o relatório de um detector de alterações, ele instruirá o componente correspondente a renderizar novamente e atualizar o DOM de acordo.

Estratégias de detecção de alterações

Tipos de valor versus tipos de referência

Para entender o que é uma estratégia de detecção de alterações e por que ela funciona, devemos primeiro entender a diferença entre tipos de valor e tipos de referência em JavaScript. Se você já sabe como isso funciona, pode pular esta seção.

Para começar, vamos revisar os tipos de valor e os tipos de referência e suas classificações.

Tipos de valor

  • boleano

  • Nulo

  • Indefinido

  • Número

  • Corda

Para simplificar, pode-se imaginar que esses tipos simplesmente armazenam seu valor na memória da pilha (o que tecnicamente não é verdade, mas é suficiente para este artigo). Veja a memória da pilha e seus valores na imagem abaixo por exemplo.

Memória de pilha

Tipos de referência

  • Matrizes

  • Objetos

  • Funções

Esses tipos são um pouco mais complicados, pois armazenam uma referência na memória da pilha, que aponta para seu valor real na memória heap. Você pode ver como a memória de pilha e a memória de heap funcionam juntas na imagem de exemplo abaixo. Vemos que a memória da pilha faz referência aos valores reais do tipo de referência na memória heap.

Memória de pilha e memória de pilha

A distinção importante a ser feita entre tipos de valor e tipos de referência é que, para ler o valor do tipo de valor, basta consultar a memória da pilha, mas para ler o valor de um tipo de referência, precisamos primeiro consulte a memória da pilha para obter a referência e, em seguida, use essa referência para consultar a memória heap para localizar o valor do tipo de referência.

Estratégia padrão

Como afirmamos anteriormente, o Angular monitora as alterações no modelo para garantir que ele capture todas as alterações. Ele verificará quaisquer diferenças entre o estado anterior e o estado atual do modelo geral do aplicativo.

A pergunta que o Angular faz na estratégia de detecção de alterações padrão é: algum valor no modelo mudou? Mas para um tipo de referência, podemos implementar estratégias para que possamos fazer uma pergunta melhor. É aí que entra a estratégia de detecção de alterações OnPush.

Estratégia OnPush

A ideia principal por trás da estratégia OnPush se manifesta a partir da percepção de que, se tratarmos os tipos de referência como objetos imutáveis, podemos detectar se um valor mudou muito mais rapidamente. Quando um tipo de referência é imutável, isso significa que toda vez que for atualizado, a referência na memória da pilha terá que ser alterada. Agora podemos simplesmente verificar: A referência (na pilha) do tipo de referência mudou? Se sim, só então verifique todos os valores (no heap). Consulte os diagramas de pilha de pilha anteriores se isso for confuso.

A estratégia OnPush basicamente faz duas perguntas em vez de uma. A referência do tipo de referência mudou? Se sim, os valores na memória heap foram alterados?

Por exemplo, suponha que temos um array imutável com 30 elementos e queremos saber se há alguma alteração. Sabemos que, para que haja atualizações no array imutável, a referência (na pilha) dele teria que ser alterada. Isso significa que podemos verificar inicialmente se a referência ao array é diferente, o que potencialmente nos pouparia de fazer mais 30 verificações (no heap) para determinar qual elemento é diferente. Isso é chamado de estratégia OnPush.

Então, você pode perguntar, o que significa tratar os tipos de referência como imutáveis? Isso significa que nunca definimos a propriedade de um tipo de referência, mas reatribuímos o valor todos juntos. Veja abaixo:

Tratando objetos como mutáveis:

 static mutable() { var before = {foo: "bar"}; var current = before; current.foo = "hello"; console.log(before === current); // => true }

Tratando objetos como imutáveis:

 static mutable() { var before = {foo: "bar"}; var current = before; current = {foo "hello"}; console.log(before === current); // => false }

Observe que, nos exemplos acima, estamos “tratando” os tipos de referência como imutáveis ​​por convenção, então no final ainda estamos trabalhando com objetos mutáveis, mas apenas “fingindo” que eles são imutáveis.

Então, como você implementa a estratégia OnPush para um componente? Tudo o que você precisa fazer é adicionar o parâmetro changeDetection em sua anotação @Component.

 import {ChangeDetectionStrategy, Component} from '@angular/core'; @Component({ // ... changeDetection: ChangeDetectionStrategy.OnPush }) export class OnPushComponent { // ... }

Imutável.js

É uma boa ideia impor a imutabilidade se alguém decidir usar a estratégia OnPush em um componente Angular. É aí que entra o Immutable.js.

Immutable.js é uma biblioteca criada pelo Facebook para imutabilidade em JavaScript. Eles têm muitas estruturas de dados imutáveis, como List, Map e Stack. Para os propósitos deste artigo, Lista e Mapa serão ilustrados. Para mais referência, consulte a documentação oficial aqui.

Para adicionar Immutable.js aos seus projetos, certifique-se de entrar no seu terminal e executar:

 $ npm install immutable --save

Certifique-se também de importar as estruturas de dados que você está usando de Immutable.js no componente em que você está usando.

 import {Map, List} from 'immutable';

É assim que um mapa Immutable.js pode ser usado:

 var foobar = {foo: "bar"}; var immutableFoobar = Map(foobar); console.log(immutableFooter.get("foo")); // => bar

E, um Array pode ser usado:

 var helloWorld = ["Hello", "World!"]; var immutableHelloWorld = List(helloWorld); console.log(immutableHelloWorld.first()); // => Hello console.log(immutableHelloWorld.last()); // => World! helloWorld.push("Hello Mars!"); console.log(immutableHelloWorld.last()); // => Hello Mars!

Desvantagens do uso de Immutable.js

Existem algumas desvantagens discutíveis principais de usar Immutable.js.

Como você deve ter notado, é um pouco complicado usar sua API, e um desenvolvedor JavaScript tradicional pode não gostar disso. Um problema mais sério tem a ver com a impossibilidade de implementar interfaces para seu modelo de dados, pois Immutable.js não oferece suporte a interfaces.

Embrulhar

Você pode estar se perguntando por que a estratégia OnPush não é a estratégia padrão para Angular. Presumo que seja porque o Angular não queria forçar os desenvolvedores de JavaScript a trabalhar com objetos imutáveis. Mas, isso não significa que você está proibido de usá-lo.

Se isso é algo que você deseja aproveitar em seu próximo projeto da Web, agora você sabe como o Angular facilita a mudança para uma estratégia diferente de detecção de alterações.