Rilevamento delle modifiche angolari e la strategia OnPush
Pubblicato: 2022-03-11Hai iniziato a utilizzare Angular per tutti i tuoi progetti preferiti. Sai cosa ha da offrire Angular e come puoi sfruttarlo per creare fantastiche app web. Ma ci sono alcune cose su Angular e conoscerle può renderti migliore nell'uso di Angular per i tuoi progetti.
Essendo il flusso di dati al centro di quasi tutte le cose Angular, il rilevamento delle modifiche è qualcosa di cui vale la pena conoscere, poiché ti aiuterà a tracciare i bug molto più facilmente e ti darà l'opportunità di ottimizzare ulteriormente le tue app quando lavori con un set di dati complesso.
In questo articolo imparerai come Angular rileva le modifiche nelle sue strutture dati e come puoi renderle immutabili per ottenere il massimo dalle strategie di rilevamento delle modifiche di Angular.
Rilevamento modifiche in angolare
Quando modifichi uno qualsiasi dei tuoi modelli, Angular rileva le modifiche e aggiorna immediatamente le viste. Questo è il rilevamento delle modifiche in Angular. Lo scopo di questo meccanismo è assicurarsi che le viste sottostanti siano sempre sincronizzate con i modelli corrispondenti. Questa caratteristica fondamentale di Angular è ciò che fa funzionare il framework ed è in parte il motivo per cui Angular è una scelta accurata per lo sviluppo di moderne app Web.
Un modello in Angular può cambiare a causa di uno qualsiasi dei seguenti scenari:
Eventi DOM (clic, passaggio del mouse sopra, ecc.)
Richieste AJAX
Timer (setTimer(), setInterval())
Rilevatori di cambiamento
Tutte le app Angular sono costituite da un albero gerarchico di componenti. In fase di esecuzione, Angular crea una classe di rilevatore di modifiche separata per ogni componente nell'albero, che alla fine forma una gerarchia di rilevatori di modifiche simile all'albero gerarchico dei componenti.
Ogni volta che viene attivato il rilevamento delle modifiche, Angular scende in questo albero di rilevatori di modifiche per determinare se qualcuno di essi ha segnalato delle modifiche.
Il ciclo di rilevamento delle modifiche viene sempre eseguito una volta per ogni modifica rilevata e parte dal rilevatore di modifiche alla radice e scende fino in fondo in modo sequenziale. Questa scelta di progettazione sequenziale è utile perché aggiorna il modello in modo prevedibile poiché sappiamo che i dati dei componenti possono provenire solo dal suo genitore.
I rilevatori di modifiche forniscono un modo per tenere traccia degli stati precedenti e attuali del componente, nonché della sua struttura al fine di segnalare le modifiche ad Angular.
Se Angular ottiene il rapporto da un rilevatore di modifiche, indica al componente corrispondente di eseguire nuovamente il rendering e aggiornare il DOM di conseguenza.
Strategie di rilevamento delle modifiche
Valore e tipi di riferimento
Per capire cos'è una strategia di rilevamento delle modifiche e perché funziona, dobbiamo prima capire la differenza tra tipi di valore e tipi di riferimento in JavaScript. Se hai già familiarità con il funzionamento, puoi saltare questa sezione.
Per iniziare, esaminiamo i tipi di valore e i tipi di riferimento e le relative classificazioni.
Tipi di valore
booleano
Nullo
Non definito
Numero
Corda
Per semplicità, si può immaginare che questi tipi memorizzino semplicemente il loro valore nella memoria dello stack (il che tecnicamente non è vero ma è sufficiente per questo articolo). Ad esempio, vedere la memoria dello stack e i suoi valori nell'immagine seguente.
Tipi di riferimento
Matrici
Oggetti
Funzioni
Questi tipi sono un po' più complicati in quanto memorizzano un riferimento nella memoria dello stack, che punta al loro valore effettivo nella memoria dell'heap. Puoi vedere come la memoria dello stack e la memoria heap funzionano insieme nell'immagine di esempio seguente. Vediamo che la memoria dello stack fa riferimento ai valori effettivi del tipo di riferimento nella memoria heap.
L'importante distinzione da fare tra tipi di valore e tipi di riferimento è che, per leggere il valore del tipo di valore, dobbiamo solo interrogare la memoria dello stack, ma per leggere il valore di un tipo di riferimento, dobbiamo prima interrogare la memoria dello stack per ottenere il riferimento e quindi utilizzare tale riferimento per interrogare la memoria heap per individuare il valore del tipo di riferimento.
Strategia predefinita
Come affermato in precedenza, Angular monitora le modifiche sul modello per assicurarsi che catturi tutte le modifiche. Verificherà eventuali differenze tra lo stato precedente e lo stato attuale del modello applicativo generale.

La domanda che Angular pone nella strategia di rilevamento delle modifiche predefinita è: è cambiato qualche valore nel modello? Ma per un tipo di riferimento, possiamo implementare strategie in modo da poter porre una domanda migliore. È qui che entra in gioco la strategia di rilevamento delle modifiche di OnPush.
Strategia OnPush
L'idea principale alla base della strategia OnPush si manifesta dalla consapevolezza che se trattiamo i tipi di riferimento come oggetti immutabili, possiamo rilevare se un valore è cambiato molto più velocemente. Quando un tipo di riferimento è immutabile, significa che ogni volta che viene aggiornato, il riferimento nella memoria dello stack dovrà cambiare. Ora possiamo semplicemente controllare: il riferimento (nello stack) del tipo di riferimento è cambiato? Se sì, solo allora controlla tutti i valori (nell'heap). Fare riferimento ai precedenti diagrammi di heap dello stack se ciò crea confusione.
La strategia OnPush pone fondamentalmente due domande invece di una. Il riferimento del tipo di riferimento è cambiato? Se sì, i valori nella memoria heap sono cambiati?
Ad esempio, supponiamo di avere un array immutabile con 30 elementi e di voler sapere se ci sono modifiche. Sappiamo che, affinché ci siano aggiornamenti all'array immutabile, il riferimento (nello stack) di esso dovrebbe essere cambiato. Ciò significa che inizialmente possiamo verificare se il riferimento all'array è diverso, il che ci salverebbe potenzialmente dall'eseguire altri 30 controlli (nell'heap) per determinare quale elemento è diverso. Questa è chiamata strategia OnPush.
Quindi, potresti chiedere, cosa significa trattare i tipi di riferimento come immutabili? Significa che non impostiamo mai la proprietà di un tipo di riferimento, ma riassegnamo il valore tutti insieme. Vedi sotto:
Trattare gli oggetti come mutevoli:
static mutable() { var before = {foo: "bar"}; var current = before; current.foo = "hello"; console.log(before === current); // => true }
Trattare gli oggetti come immutabili:
static mutable() { var before = {foo: "bar"}; var current = before; current = {foo "hello"}; console.log(before === current); // => false }
Nota che, negli esempi precedenti, stiamo "trattando" i tipi di riferimento come immutabili per convenzione, quindi alla fine stiamo ancora lavorando con oggetti mutabili, ma semplicemente "fingendo" che siano immutabili.
Quindi, come si implementa la strategia OnPush per un componente? Tutto quello che devi fare è aggiungere il parametro changeDetection
nella loro annotazione @Component.
import {ChangeDetectionStrategy, Component} from '@angular/core'; @Component({ // ... changeDetection: ChangeDetectionStrategy.OnPush }) export class OnPushComponent { // ... }
Immutabile.js
È una buona idea imporre l'immutabilità se si decide di utilizzare la strategia OnPush su un componente angolare. È qui che entra in gioco Immutable.js.
Immutable.js è una libreria creata da Facebook per l'immutabilità in JavaScript. Hanno molte strutture di dati immutabili, come List, Map e Stack. Ai fini di questo articolo verranno illustrati Elenco e Mappa. Per ulteriori informazioni, consultare la documentazione ufficiale qui.
Per aggiungere Immutable.js ai tuoi progetti, assicurati di entrare nel tuo terminale ed eseguire:
$ npm install immutable --save
Assicurati anche di importare le strutture di dati che stai utilizzando da Immutable.js nel componente in cui lo stai utilizzando.
import {Map, List} from 'immutable';
Ecco come è possibile utilizzare una mappa Immutable.js:
var foobar = {foo: "bar"}; var immutableFoobar = Map(foobar); console.log(immutableFooter.get("foo")); // => bar
Inoltre, è possibile utilizzare un array:
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!
Svantaggi dell'utilizzo di Immutable.js
Ci sono un paio di principali inconvenienti discutibili dell'utilizzo di Immutable.js.
Come avrai notato, è un po' complicato usare la sua API e a uno sviluppatore JavaScript tradizionale potrebbe non piacere. Un problema più serio ha a che fare con l'impossibilità di implementare interfacce per il tuo modello di dati poiché Immutable.js non supporta le interfacce.
Incartare
Potresti chiederti perché la strategia OnPush non è la strategia predefinita per Angular. Presumo sia perché Angular non voleva costringere gli sviluppatori JavaScript a lavorare con oggetti immutabili. Ma ciò non significa che ti sia vietato usarlo.
Se questo è qualcosa che vuoi sfruttare nel tuo prossimo progetto web, ora sai quanto sia facile con Angular passare a una diversa strategia di rilevamento delle modifiche.