Jusqu'à présent, la plupart des "passe-partout" et certains articles sur react/redux que j'ai vus encouragent l'utilisation de immutable.js pour corriger la mutabilité. Je compte personnellement sur Object.assign
ou des opérateurs répartis pour gérer cela, donc ne voient pas vraiment l'avantage dans immutable.js car il ajoute un apprentissage supplémentaire et décale un peu des techniques Vanilla js utilisées pour la mutabilité. J'essayais de trouver des raisons valables pour un changement, mais je n'y suis pas parvenu, je demande donc ici pourquoi il est si populaire.
C'est une question d'efficacité.
Une structure de données persistante conserve les versions précédentes d'elle-même lorsqu'elle est mutée en produisant toujours une nouvelle structure de données. Pour éviter un clonage coûteux, seule la différence avec la structure de données précédente est stockée, tandis que l'intersection est partagée entre eux. Cette stratégie est appelée partage structurel. Par conséquent, les structures de données persistantes sont beaucoup plus efficaces que le clonage avec Object.assign
ou l'opérateur d'étalement.
Malheureusement, Javascript ne prend pas en charge nativement les structures de données persistantes. C'est la raison pour laquelle immutable.js existe et que ses objets diffèrent grandement des anciens Javascript Object
s. Cela conduit à un code plus détaillé et à de nombreuses conversions de structures de données persistantes en structures de données Javascript natives.
Quand les avantages du partage structurel (efficacité) de immutable.js dépassent-ils ses inconvénients (verbosité, conversions)?
Je suppose que la bibliothèque n'est rentable que dans les grands projets avec des objets et des collections nombreux et étendus, lorsque le clonage de structures de données entières et la collecte des ordures deviennent plus chers.
J'ai créé les tests de performance pour plusieurs bibliothèques immuables, le script et les résultats sont situés à l'intérieur de immutable-assign (projet GitHub) , ce qui montre que immutable.js est optimisé pour les opérations d'écriture, plus rapides que Object.assign (), cependant, elles sont plus lentes pour les opérations de lecture. Voici le résumé des résultats des benchmarks:
-- Mutable
Total elapsed = 50 ms (read) + 53 ms (write) = 103 ms.
-- Immutable (Object.assign)
Total elapsed = 50 ms (read) + 2149 ms (write) = 2199 ms.
-- Immutable (immutable.js)
Total elapsed = 638 ms (read) + 1052 ms (write) = 1690 ms.
-- Immutable (seamless-immutable)
Total elapsed = 31 ms (read) + 91302 ms (write) = 91333 ms.
-- Immutable (immutable-assign (created by me))
Total elapsed = 50 ms (read) + 2173 ms (write) = 2223 ms.
Par conséquent, l'utilisation ou non de immutable.js dépendra du type de votre application et de son rapport lecture/écriture. Si vous avez beaucoup d'opérations d'écriture, alors immutable.js sera une bonne option.
Idéalement, vous devez profiler votre application avant d'introduire une optimisation des performances, cependant, l'immuabilité est l'une de ces décisions de conception qui doit être décidée tôt. Lorsque vous commencez à utiliser immutable.js , vous devez l'utiliser dans l'ensemble de votre application pour obtenir des avantages en termes de performances, car l'interopérabilité avec des objets JS simples à l'aide de fromJS () et toJS () est très coûteuse.
Je pense que le principal avantage d'ImmutableJs réside dans ses structures de données et sa vitesse. Bien sûr, cela renforce également l'immuabilité, mais vous devriez quand même le faire, ce n'est donc qu'un avantage supplémentaire.
Par exemple, supposons que vous ayez un très gros objet dans votre réducteur et que vous souhaitiez modifier une très petite partie de cet objet. En raison de l'immuabilité, vous ne pouvez pas modifier directement l'objet, mais vous devez créer une copie de l'objet. Pour ce faire, copiez tout (dans ES6 à l'aide de l'opérateur d'étalement).
Donc quel est le problème? La copie d'objets très volumineux est très lente. Les structures de données dans ImmutableJS font quelque chose appelé partage structurel où vous ne modifiez vraiment que les données que vous voulez. Les autres données que vous ne modifiez pas sont partagées entre les objets, donc elles ne sont pas copiées.
Il en résulte des structures de données très efficaces, avec des écritures rapides.
ImmutableJS offre également des comparaisons faciles pour les objets profonds. Par exemple
const a = Immutable.Map({ a: Immutable.Map({ a: 'a', b: 'b'}), b: 'b'});
const b = Immutable.Map({ a: Immutable.Map({ a: 'a', b: 'b'}), b: 'b'});
console.log(a.equals(b)) // true
Sans cela, vous auriez besoin d'une sorte de fonction de comparaison approfondie, qui prendrait également beaucoup de temps, alors que les nœuds racine contiennent ici un hachage de l'ensemble de la structure de données (ne me citez pas à ce sujet, voici comment je m'en souviens) , mais les comparaisons sont toujours instantanées), donc les comparaisons sont toujours O(1)
c'est-à-dire instantanées, quelle que soit la taille de l'objet.
Cela peut être particulièrement utile dans la méthode React shouldComponentUpdate
, où vous pouvez simplement comparer les accessoires à l'aide de cette fonction equals
, qui s'exécute instantanément.
Bien sûr, il y a aussi des inconvénients, si vous mélangez des structures immuables et des objets réguliers, il peut être difficile de dire quoi. De plus, votre base de code est jonchée de la syntaxe immutableJS, qui est différente du Javascript standard.
Un autre inconvénient est que si vous n'utilisez pas d'objets profondément imbriqués, cela sera un peu plus lent que les anciens js simples, car les structures de données ont un certain temps système.
Juste mes 2 cents.
L'avantage de immutable.js est qu'il applique un magasin redux immuable (vous pourriez oublier d'étendre ou de faire un travail bâclé pour protéger votre magasin des modifications) et simplifie le réducteurs beaucoup par rapport à Object.assign
ou opérateurs spread (les deux sont d'ailleurs peu profonds!).
Cela étant dit, j'ai utilisé redux + immuable dans un grand projet Angular 1.x et il y a des inconvénients: problèmes de performances, on ne sait pas ce qui est immuable et ce qui ne l'est pas, Angular ne peut pas utiliser les structures immutable.js dans ng-repeat
, etc. Pas sûr de React cependant, j'aimerais entendre des opinions.
Il ne s'agit pas seulement de gains de performances via des structures de données persistantes. L'immuabilité est une qualité hautement souhaitable en soi, car elle élimine complètement les bugs causés par des mutations accidentelles. De plus, il est agréable d'avoir des structures de données sensées avec une API pratique en Javascript, quelque chose de réellement utilisable et immuable.js est bien en avance sur les objets et les tableaux vanillla, même avec les ajouts récents d'ES6 et ES7. C'est comme jQuery - il est si populaire car son API est vraiment géniale et facile à utiliser par rapport à Vanilla DOM API (qui n'est qu'un cauchemar). Bien sûr, vous pouvez essayer de rester immuable avec les objets Vanilla, mais alors vous ne devez pas oublier Object.assign et le tableau réparti CHAQUE FOIS, et avec immutable.js, vous ne pouvez tout simplement pas vous tromper, toutes les mutations ici sont explicites
immutable.js vous offre de nombreux utilitaires qui retournent toujours un nouvel objet.
exemple:
var Immutable = require('immutable');
var map1 = Immutable.Map({a:1, b:2, c:3});
var map2 = map1.set('b', 50);
map1.get('b'); // 2
map2.get('b'); // 50
Pour juste réagir/redux, je pense personnellement que Object.assign est assez puissant, mais dans certains cas, l'utilisation de cette bibliothèque pourrait vous faire économiser quelques lignes de code.