La meilleure façon de travailler avec le DOM

Pendant que j'écrivais sur Solid , j'ai eu l'opportunité d'estimer le nombre de tentatives d'optimisation des performances sur les graphes (benchmarks).

DOM est un développement de goulot d'étranglement frontal. Différentes solutions peuvent conduire à des résultats similaires. De nouvelles bibliothèques apparaissent chaque semaine, incorporant les meilleures des précédentes, pour faire la combinaison parfaite. Après un certain temps, mes yeux commencent à fuir une telle variété de solutions.

Ne vous méprenez pas. J'adore voir de nouvelles idées prendre vie, mais elles ont toutes des défauts et entraînent parfois une perte de commodité ou de productivité. Je comprends les conséquences d'une telle décision, mais je veux partager mes observations.

Quelle est la façon la plus rapide de travailler avec le DOM:

  • Dom virtuel
  • LittĂ©raux de modèle balisĂ©s
  • Observables Ă  grain fin

Comparaison


JS Frameworks Benchmark est le meilleur projet open source pour comparer les performances des frameworks d'interface utilisateur JavaScript. Il est préférable d'exécuter les tests localement au lieu d'utiliser les résultats officiels. Les résultats peuvent varier selon la machine. Comme je teste sur une machine faible, les performances chuteront sensiblement.

Je prendrai les meilleures approches dans le rendu d'un arbre DOM pour illustrer la redondance de certaines solutions. Je vais le faire en utilisant la capacité de Solid à prendre en charge différentes options de rendu afin d'imposer des coûts à mesure que les variables changent et de le comparer avec des résultats similaires d'autres cadres. Jetons-y un œil.

Variétés de solide:

  • solid - version du framework avec ES2015 proxy setter en plus des fonctions de suivi des changements intĂ©grĂ©es dans les modèles de nĹ“uds DOM clonĂ©s. Ceci est rĂ©alisĂ© en prĂ©compilant les modèles JSX (Code)
  • signaux solides - cette version est la mĂŞme que la prĂ©cĂ©dente, mais des signaux bruts sont utilisĂ©s Ă  la place des proxys. Cela complique l'utilisation de la bibliothèque, mais Ă  la fin, nous obtenons un ensemble plus petit et de meilleures performances (Code)
  • allumĂ© en continu - cette version n'utilise pas la prĂ©compilation JSX lors de l'exĂ©cution (Code)
  • solid-h - Cette version utilise HyperScript pour crĂ©er `document.createElement` Ă  la volĂ©e. Le reste utilise la mĂŞme implĂ©mentation que Solid (Code)

Autres bibliothèques:

  • domc — -, DOM DSL (domain specific language) HTML, index.html (Code)
  • surplus — JSX `document.createElement`. Solid (Code)
  • ivi — Inferno, DOM. HyperScript Helpers-esque (Code)
  • lit-html — , c . Tagged Template Literals DOM- (Code)
  • inferno est le plus rapide des clones React et l'une des bibliothèques Virtual DOM les plus rapides. Utilise des directives JSX spĂ©ciales pour de meilleures performances (Code)

Certains de vos frameworks préférés peuvent ne pas être ici, mais cette liste affiche des versions optimisées de toutes les techniques que vous verrez dans les frameworks les plus populaires. Vous pouvez le considérer comme un indicateur pour évaluer les capacités maximales de votre bibliothèque. Si vous souhaitez comparer les performances des frameworks populaires, je recommande ce tableau.

À titre de comparaison, je voudrais ajouter Web Assembly. Malheureusement, au moment d'écrire ces lignes, les enregistrements WASM étaient une implémentation vanille sans abstractions de haut niveau. (Plus tard, ils ont ajouté wasm-bindgen au framework - environ Translator)

résultats


HyperScript (inferno, ivi, solid-h)


HyperScript affiche le balisage comme une composition de fonctions (comme h ou React.createElement). Par exemple:

h('div', {id: 'my-element'}, [
  h('span', 'Hello'),
  h('span', 'John')
])

Les frameworks DOM virtuels ont cette propriété. Même s'ils utilisent JSX ou d'autres moteurs de modèle DSL - sous le capot, ils sont toujours convertis en méthodes de rendu élément par élément. Ceci est utilisé pour construire une arborescence DOM virtuelle pour chaque cycle de rendu. Mais, comme illustré ici, les fonctions rendues peuvent être utilisées pour créer un graphe de dépendances réactives, comme dans le cas de Solid.



Comme vous pouvez le voir, les bibliothèques avec un DOM virtuel sont beaucoup plus rapides. Solid perd des performances en raison de la création excessive d'un graphe réactif. Notez la différence dans les repères # 1, # 2, # 7, # 8, # 9.



La mémoire est moins convaincante. Inferno et cette version de Solid affichent à peu près le même résultat. Alors qu'ivi utilise plus de mémoire.
, Solid, , VDOM. Solid , DOM, DOM. Solid JSX DOM . , . Solid fine grained evaluation . - .
—
Les cadres de suivi des mises à jour réactifs affichent de meilleurs résultats lors de la mise à jour des lignes. Ce graphique expliquerait la popularité du VDOM ces dernières années. Il suffit de dire que si vous utilisez HyperScript avec cette mise à jour, vous feriez mieux de basculer vers Virtual DOM.

Modèles de chaînes (domc, lit-html, solid-lit)


Chaque bibliothèque ici a quelque chose en commun. Ils sont rendus sur la base de modèles d'éléments de clonage, exécutés au moment de l'exécution et n'utilisent pas de VDOM. Mais ils ont encore des différences. DomC et lit-html utilisent des différences descendantes similaires à Virtual DOM, tandis que Solid utilise un graphique réactif. Lit-html divise les modèles en parties. DomC et Solid compile le modèle dans des chemins distincts lors de l'exécution et les met à jour.



Cette catégorie a la gamme de performances la plus large. DomC est le plus rapide et lit-html est le plus lent. Solid Lit est au milieu. DomC démontre que la simplicité du code conduit aux meilleures performances.

DomC s'affaisse uniquement dans la section # 4 car il calcule la différence de nœuds, ce qui devient plus compliqué à mesure que la profondeur augmente. C'est assez rapide, mais vous devez valider les résultats sur les mégadonnées.

Solid Lit est plus productif que Solid HyperScript. La compilation instantanée en runtime supprime les inconvénients de la création d'un graphe réactif, permettant au framework de rattraper ivi, la bibliothèque VDOM la plus rapide (voir le tableau complet à la fin de l'article).



DomC a montré de bons résultats en termes de consommation de mémoire. Cela est dû au clonage des éléments du modèle. Il est à noter que la génération de code lors de l'exécution peut avoir des coûts de performances minimes par rapport à la compilation au stade de la construction. C'est peut-être une comparaison injuste pour lit-html car le framework n'utilise pas cette technique. Mais il est juste de dire que lit-html ou des bibliothèques similaires comme hyperHTML ou lighterHTML ne sont pas le meilleur moyen d'implémenter des littéraux de modèle balisés. Et vous pouvez obtenir de bons résultats même en exécution sans VDOM.

JSX précompilé (solide, signaux solides, surplus)


Ces bibliothèques utilisent JSX, qui se compile dans un DOM ou un graphe réactif au stade de la construction. Les modèles peuvent être n'importe quoi, mais JSX fournit une arborescence de syntaxe propre qui améliore l'expérience du développeur.



Ce groupe a des résultats similaires, mais la différence est très importante. Tous les trois utilisent la même bibliothèque pour gérer S.js état . En utilisant l'exemple Solid Signals, vous pouvez voir que les fonctions de suivi avec le clonage des éléments de modèle donnent de meilleures performances. L'implémentation standard de Solid est surchargée à l'aide des proxys ES2015, ce qui aggrave le résultat sur tous les graphiques. Surplus utilise `document.createElement`, ce qui dégrade les performances sur les tests où les lignes # 1, # 2, # 7, # 8 sont créées.



La consommation de mémoire a des résultats similaires. Dans ce cas, les proxys ajoutent plus de complexité que les éléments de modèle de clonage.

La conclusion ici est que les proxys dégradent les performances et plus de bibliothèques devraient cloner les modèles. D'un autre côté, vous pouvez considérer une petite perte de performance due aux procurations comme un investissement. L'exemple Solid a la plus petite quantité de code parmi d'autres exemples - seulement 66 lignes, il a 13% moins d'espaces non blancs que Svelte - une bibliothèque qui se targue de son minimalisme.

Meilleur de sa catégorie (domc, ivi, signaux solides, vanillajs)


Prenons maintenant les gagnants de chaque catégorie et comparons-les avec l'exemple brutal, efficace et écrit à la main en JavaScript vanille. Chaque implémentation représente l'une des solutions de suivi d'état les plus populaires. Vous pouvez même faire une analogie entre ces bibliothèques et les Big Three: Solid → Vue, DomC → Angular, ivi → React. Vous obtiendrez ce résultat si vous supprimez tout ce qui est superflu, à l'exception du rendu, et que vous vous débarrassez du code 60-200kb.



DomC et Solid sont proches en termes de performances, ivi est nettement en retard, mais DomC, dans l'ensemble, est plus rapide. Sa complexité par rapport à vanillaJS est sensiblement moindre, mais elle est moins efficace avec des mises à jour partielles. Seul ce critère n'est pas indicatif. Quiconque pense que le VDOM est lent ou a des complications inutiles devrait le vérifier par lui-même.

La plupart des bibliothèques n'auront jamais ce genre de performances.



DomC est également leader dans le graphique avec la mémoire. Le solide à grain fin surpasse le VDOM ivi en termes de consommation de mémoire.

Fait intéressant, ces bibliothèques ne sont pas bien pires que vanillaJS, quelle que soit la méthode. Ils sont tous très rapides.

Taille du paquet


Enfin, je voudrais aborder la taille du paquet. De nombreux tests réels se concentrent uniquement sur ces mesures. Oui, la taille de l'ensemble est importante et a une corrélation directe avec les performances, mais quelle est la différence? Je soupçonne que la complexité du code est plus importante que la taille.



Conclusion


Comme d'habitude, les résultats dans de tels graphiques ne sont jamais complètement convaincants. Le processus lui-même et les conclusions que nous tirons sont importants. Dans ce cas, nous voyons que le DOM lui-même est un gros goulot d'étranglement en matière de performances. À tel point qu'il n'y a pas de technique sans équivoque pour le contourner.


Christoper Lambert comme The Highlander

Non, ce n'est pas si simple. Ni le DOM ni le VDOM ne sont lents. Mais je crois qu'ils se valent. J'avoue que la rhétorique du VDOM Performance React m'a conduit à ces réflexions. L'ignorance des opinions sur ce sujet est exaspérante.

L'affirmation selon laquelle la VDOM est lente est due à une mauvaise sensibilisation. Rendre VDOM et calculer la différence d'état est une complication par rapport à ne pas le faire. Mais son absence est-elle évolutive? Et comment faire des changements de données?

Je vois qu'il y a une exception dans chaque règle. En général, la précompilation combinée avec Fine-Grained dans des cadres réactifs est la solution la plus rapide. Mais DomC affiche de hautes performances sans lui. Les méthodes JS natives, telles que le clonage d'éléments de modèle avec des littéraux de modèle étiquetés, peuvent être la meilleure solution pour implémenter lit-html de grandes sociétés (Google). Mais c'est l'un des cadres les plus lents de ce tableau et même pas la meilleure mise en œuvre de ces technologies. Svelte est considérée comme la bibliothèque la plus rapide de la communauté, mais elle ne pouvait même pas rivaliser étroitement avec les solutions présentées.

Si la programmation réactive gagne, cela ne signifie pas que toutes les bibliothèques réactives sont rapides ou que les métriques signifient tout. Malgré la comparaison approfondie de cet article, je pense qu'en réalité, il existe des bibliothèques rapides et des bibliothèques plus lentes. Même si nous trouvons une super technologie, nous ferons toujours face à ses limites.

Résultats des tests de toutes les bibliothèques dans une seule table:


All Articles