Détection des changements angulaires et stratégie OnPush

Publié: 2022-03-11

Vous avez commencé à utiliser Angular pour tous vos projets préférés. Vous savez ce que Angular a à offrir et comment vous pouvez en tirer parti pour créer des applications Web étonnantes. Mais, il y a certaines choses à propos d'Angular, et les connaître peut vous aider à mieux utiliser Angular pour vos projets.

Le flux de données étant au centre de presque tout ce qui concerne Angular, la détection des changements vaut la peine d'être connue, car elle vous aidera à tracer les bugs beaucoup plus facilement et vous donnera l'opportunité d'optimiser davantage vos applications lorsque vous travaillez avec un ensemble de données complexe.

Détection des changements angulaires et stratégie OnPush

Dans cet article, vous apprendrez comment Angular détecte les changements dans ses structures de données et comment vous pouvez les rendre immuables pour tirer le meilleur parti des stratégies de détection des changements d'Angular.

Détection de changement dans Angular

Lorsque vous modifiez l'un de vos modèles, Angular détecte les modifications et met immédiatement à jour les vues. C'est la détection de changement dans Angular. Le but de ce mécanisme est de s'assurer que les vues sous-jacentes sont toujours synchronisées avec leurs modèles correspondants. Cette fonctionnalité essentielle d'Angular est ce qui fait fonctionner le framework et est en partie la raison pour laquelle Angular est un choix judicieux pour développer des applications Web modernes.

Un modèle dans Angular peut changer à la suite de l'un des scénarios suivants :

  • Événements DOM (clic, survol, etc.)

  • Requêtes AJAX

  • Minuteries (setTimer(), setInterval())

Changer les détecteurs

Toutes les applications Angular sont constituées d'une arborescence hiérarchique de composants. Au moment de l'exécution, Angular crée une classe de détecteurs de changement distincte pour chaque composant de l'arborescence, qui forme ensuite une hiérarchie de détecteurs de changement similaire à l'arborescence hiérarchique des composants.

Chaque fois que la détection de changement est déclenchée, Angular parcourt cette arborescence de détecteurs de changement pour déterminer si l'un d'entre eux a signalé des changements.

Le cycle de détection de changement est toujours effectué une fois pour chaque changement détecté et commence à partir du détecteur de changement racine et descend tout le long de manière séquentielle. Ce choix de conception séquentielle est agréable car il met à jour le modèle de manière prévisible puisque nous savons que les données du composant ne peuvent provenir que de son parent.

Modifier la hiérarchie des détecteurs

Les détecteurs de changement permettent de suivre les états précédents et actuels du composant ainsi que sa structure afin de signaler les changements à Angular.

Si Angular obtient le rapport d'un détecteur de changement, il demande au composant correspondant de restituer et de mettre à jour le DOM en conséquence.

Changer les stratégies de détection

Types de valeur et de référence

Afin de comprendre ce qu'est une stratégie de détection de changement et pourquoi elle fonctionne, nous devons d'abord comprendre la différence entre les types de valeur et les types de référence en JavaScript. Si vous savez déjà comment cela fonctionne, vous pouvez ignorer cette section.

Pour commencer, examinons les types de valeur et les types de référence et leurs classifications.

Types de valeur

  • booléen

  • Nul

  • Indéfini

  • Nombre

  • Chaîne de caractères

Pour simplifier, on peut imaginer que ces types stockent simplement leur valeur sur la mémoire de la pile (ce qui n'est techniquement pas vrai mais c'est suffisant pour cet article). Voir la mémoire de pile et ses valeurs dans l'image ci-dessous par exemple.

Mémoire de pile

Types de référence

  • Tableaux

  • Objets

  • Les fonctions

Ces types sont un peu plus compliqués car ils stockent une référence sur la mémoire de pile, qui pointe vers leur valeur réelle sur la mémoire de tas. Vous pouvez voir comment la mémoire de pile et la mémoire de tas fonctionnent ensemble dans l'exemple d'image ci-dessous. Nous voyons que la mémoire de pile fait référence aux valeurs réelles du type de référence dans la mémoire de tas.

Mémoire de pile et mémoire de tas

La distinction importante à faire entre les types valeur et les types référence est que, pour lire la valeur du type valeur, nous n'avons qu'à interroger la mémoire de la pile, mais pour lire la valeur d'un type référence, nous devons d'abord interrogez la mémoire de pile pour obtenir la référence, puis utilisez ensuite cette référence pour interroger la mémoire de tas afin de localiser la valeur du type de référence.

Stratégie par défaut

Comme nous l'avons indiqué précédemment, Angular surveille les modifications apportées au modèle afin de s'assurer qu'il capte toutes les modifications. Il vérifiera les différences entre l'état précédent et l'état actuel du modèle d'application global.

La question posée par Angular dans la stratégie de détection de changement par défaut est : une valeur du modèle a-t-elle changé ? Mais pour un type de référence, on peut mettre en place des stratégies pour pouvoir poser une meilleure question. C'est là qu'intervient la stratégie de détection des changements OnPush.

Stratégie OnPush

L'idée principale derrière la stratégie OnPush se manifeste à partir de la prise de conscience que si nous traitons les types de référence comme des objets immuables, nous pouvons détecter si une valeur a changé beaucoup plus rapidement. Lorsqu'un type de référence est immuable, cela signifie qu'à chaque mise à jour, la référence sur la mémoire de la pile devra changer. Maintenant, nous pouvons simplement vérifier : la référence (dans la pile) du type de référence a-t-elle changé ? Si oui, alors seulement vérifiez toutes les valeurs (sur le tas). Reportez-vous aux diagrammes de tas de pile précédents si cela prête à confusion.

La stratégie OnPush pose essentiellement deux questions au lieu d'une. La référence du type de référence a-t-elle changé ? Si oui, les valeurs de la mémoire de tas ont-elles changé ?

Par exemple, supposons que nous ayons un tableau immuable avec 30 éléments et que nous voulions savoir s'il y a des changements. Nous savons que, pour qu'il y ait des mises à jour du tableau immuable, la référence (sur la pile) de celui-ci aurait dû changer. Cela signifie que nous pouvons initialement vérifier si la référence au tableau est différente, ce qui nous éviterait potentiellement de faire 30 vérifications supplémentaires (dans le tas) pour déterminer quel élément est différent. C'est ce qu'on appelle la stratégie OnPush.

Donc, vous pourriez vous demander, que signifie traiter les types de référence comme immuables ? Cela signifie que nous ne définissons jamais la propriété d'un type de référence, mais réaffectons plutôt la valeur ensemble. Voir ci-dessous:

Traiter les objets comme mutables :

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

Traiter les objets comme immuables :

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

Notez que, dans les exemples ci-dessus, nous "traitons" les types de référence comme immuables par convention, donc à la fin nous travaillons toujours avec des objets mutables, mais "prétendons" simplement qu'ils sont immuables.

Alors, comment implémentez-vous la stratégie OnPush pour un composant ? Tout ce que vous avez à faire est d'ajouter le paramètre changeDetection dans leur annotation @Component.

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

Immuable.js

C'est une bonne idée d'imposer l'immuabilité si l'on décide d'utiliser la stratégie OnPush sur un composant angulaire. C'est là qu'intervient Immutable.js.

Immutable.js est une bibliothèque créée par Facebook pour l'immuabilité en JavaScript. Ils ont de nombreuses structures de données immuables, comme List, Map et Stack. Pour les besoins de cet article, List et Map seront illustrés. Pour plus de références, veuillez consulter la documentation officielle ici.

Afin d'ajouter Immutable.js à vos projets, assurez-vous d'aller dans votre terminal et d'exécuter :

 $ npm install immutable --save

Assurez-vous également d'importer les structures de données que vous utilisez depuis Immutable.js dans le composant où vous l'utilisez.

 import {Map, List} from 'immutable';

Voici comment une carte Immutable.js peut être utilisée :

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

Et, un tableau peut être utilisé :

 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!

Inconvénients de l'utilisation d'Immutable.js

L'utilisation d'Immutable.js présente quelques principaux inconvénients discutables.

Comme vous l'avez peut-être remarqué, il est un peu fastidieux d'utiliser son API, et un développeur JavaScript traditionnel peut ne pas aimer cela. Un problème plus grave est lié à l'impossibilité d'implémenter des interfaces pour votre modèle de données car Immutable.js ne prend pas en charge les interfaces.

Emballer

Vous demandez peut-être pourquoi la stratégie OnPush n'est pas la stratégie par défaut pour Angular. Je suppose que c'est parce qu'Angular ne voulait pas forcer les développeurs JavaScript à travailler avec des objets immuables. Mais cela ne signifie pas qu'il vous est interdit de l'utiliser.

Si c'est quelque chose que vous souhaitez exploiter dans votre prochain projet Web, vous savez maintenant à quel point Angular facilite le passage à une stratégie de détection de changement différente.