Mise à jour: cette réponse semble être assez populaire , donc j'ai pris un certain temps pour nettoyer un peu, ajouter un peu de nouvelles informations et de clarifier quelques petites choses que je pensais ne suffisait pas claire. Veuillez commenter si vous pensez que quelque chose d'autre nécessite des éclaircissements ou des mises à jour.
La plupart de vos préoccupations sont vraiment une question d'opinion et de préférence personnelle, mais je vais essayer de répondre aussi objectivement que possible:
Natif vs compilé
Écrivez JavaScript en JavaScript vanille, écrivez CSS en CSS, écrivez HTML en HTML.
À l'époque, il y avait de vives discussions pour savoir s'il fallait écrire manuellement Assembly natif ou utiliser un langage de niveau supérieur tel que C pour que le compilateur génère le code Assembly à votre place. Même avant cela, les gens refusaient de faire confiance aux assembleurs et préféraient écrire le code machine natif à la main ( et je ne plaisante pas ).
Pendant ce temps, aujourd'hui , il y a beaucoup de gens qui écrivent HTML dans Haml ou Jade , CSS Sass ou moins et JavaScript CoffeeScript ou tapuscrit . C'est là. Ça marche. Certaines personnes préfèrent, d'autres non.
Le point est qu'il n'y a rien de fondamentalement mauvais dans pas du code JavaScript en JavaScript vanille, CSS et HTML CSS en HTML. C'est vraiment une question de préférence.
DSL internes et externes
À la place, l’encapsulation de styles à l’aide de Shadow DOM React nécessite l’écriture de CSS en JavaScript. Pas beau.
Joli ou pas, il est certainement expressif. JavaScript est un langage très puissant, beaucoup plus puissant que CSS (même avec n’importe quel préprocesseur CSS). Cela dépend en quelque sorte de si vous préférez les DSL internes ou externes pour ce genre de choses. Encore une fois, une question de préférence.
(Remarque: je parlais des styles en ligne dans React auxquels il était fait référence dans la question initiale.)
Types de DSL - explication
Mise à jour: En lisant ma réponse quelque temps après l'avoir écrite, je pense que je dois expliquer ce que je veux dire ici. DSL est un langage spécifique à un domaine. Il peut être interne (en utilisant la syntaxe du langage hôte telle que JavaScript - comme par exemple React sans JSX, ou les styles en ligne de React mentionnés ci-dessus), ou externe (en utilisant une syntaxe différente. que le langage hôte - comme dans cet exemple, serait d'inclure CSS (un DSL externe) dans JavaScript).
Cela peut prêter à confusion parce que certaines publications utilisent des termes différents des mots "interne" et "externe" pour décrire ce type de DSL. Parfois, "incorporé" est utilisé au lieu de "interne" mais le mot "incorporé" peut signifier différentes choses - par exemple, Lua est décrit comme "Lua: un langage extensible incorporé", où imbriqué n'a rien à voir avec les DSL incorporés (internes) quel sens c'est tout le contraire - un DSL externe) mais cela signifie qu'il est incorporé dans le même sens que, par exemple, SQLite est une base de données incorporée. Il y a même eLua où "e" signifie "incorporé" dans un troisième sens - qu'il est destiné aux systèmes embarqués! C'est pourquoi je n'aime pas utiliser le terme «DSL intégré», car des choses comme eLua peuvent être des «DSL» qui sont «intégrés» de deux manières différentes sans être du tout un «DSL intégré»!
Pour aggraver les choses, certains projets introduisent encore plus de confusion dans la composition. Par exemple. Les modèles Flatiron sont décrits comme "sans DSL", alors qu’il s’agit en fait d’un exemple parfait de DSL interne dont la syntaxe est la suivante:map.where('href').is('/').insert('newurl');
Cela dit, j’ai écrit que "JavaScript est un langage très puissant, bien plus puissant que le CSS (même avec l’un des préprocesseurs CSS). Cela dépend de la préférence que vous accordez aux DSL internes ou externes pour ce genre de choses. une question de préférence. " Je parlais de ces deux scénarios:
Un:
/** @jsx React.DOM */
var colored = {
color: myColor
};
React.renderComponent(<div style={colored}>Hello World!</div>, mountNode);
Deux:
// SASS:
.colored {
color: $my-color;
}
// HTML:
<div class="colored">Hello World!</div>
Le premier exemple utilise ce qui a été décrit dans la question comme suit: "écriture CSS en JavaScript. Pas joli." Le deuxième exemple utilise Sass. Bien que je convienne que l’utilisation de JavaScript pour écrire des feuilles de style CSS n’est peut-être pas jolie (pour certaines définitions de "jolie"), mais il ya un avantage à le faire.
Je peux avoir des variables et des fonctions dans Sass, mais sont-elles étendues lexicalement ou dynamiquement? Sont-ils typés statiquement ou dynamiquement? Fortement ou faiblement? Qu'en est-il des types numériques? Type de coersion? Quelles valeurs sont la vérité et quelles sont la fausseté? Puis-je avoir des fonctions d'ordre supérieur? Récursion? Appels de queue? Fermetures lexicales? Sont-ils évalués en ordre normal ou en ordre applicatif? Y a-t-il une évaluation paresseuse ou enthousiaste? Les arguments des fonctions sont-ils passés par valeur ou par référence? Sont-ils mutables? Immuable? Persistant? Qu'en est-il des objets? Des classes? Des prototypes? Héritage?
Ce ne sont pas des questions triviales et pourtant je dois connaître leurs réponses si je veux comprendre le code Sass ou Less. Je connais déjà ces réponses pour JavaScript, cela signifie donc que je comprends déjà tous les DSL internes (comme les styles inline dans React) à ces mêmes niveaux. Par conséquent, si j'utilise React, je ne dois connaître qu'un seul ensemble de réponses à ceux-ci (et de nombreuses autres similaires). ) questions, tandis que quand je utilise par exemple. Sass et Handlebars, je dois alors connaître trois ensembles de ces réponses et comprendre leurs implications.
Cela ne veut pas dire qu'une façon ou l'autre est toujours meilleure, mais chaque fois que vous introduisez une autre langue dans le mix, vous payez un prix qui peut ne pas sembler aussi évident à première vue, et ce prix est une complexité.
J'espère avoir clarifié ce que je voulais dire à l'origine.
Liaison de données
Reliure à double sens
C'est un sujet vraiment intéressant et en fait aussi une question de préférence. Deux voies n'est pas toujours préférable à un sens. C'est une question de comment voulez-vous modéliser l'état mutable dans votre application. J'ai toujours considéré les liaisons bidirectionnelles comme une idée quelque peu contraire aux principes de la programmation fonctionnelle, mais la programmation fonctionnelle n'est pas le seul paradigme qui fonctionne. Certaines personnes préfèrent ce type de comportement et les deux approches semblent bien fonctionner dans la pratique. Si vous êtes intéressé par les détails des décisions de conception liées à la modélisation de l'état dans React, regardez la présentation de Pete Hunt (liée à la question) et celle de Tom Occhino et Jordan Walke qui l'expliquent très bien dans mon avis.
Mise à jour: voir aussi un autre discours de Pete Hunt: Soyez prévisible, pas correct: programmation DOM fonctionnelle .
Mise à jour 2: Il convient de noter que de nombreux développeurs se disputent contre le flux de données bidirectionnel ou la liaison bidirectionnelle, certains l'appellent même un anti-modèle. Prenons, par exemple, l’ architecture d’application Flux qui évite explicitement le modèle MVC (qui s’est avéré difficile à faire évoluer pour de grandes applications Facebook et Instagram) au profit d’un flux de données strictement unidirectionnel (voir Piste Hacker: Repenser le développement d’applications Web à Facebook sur Facebook) . Tom Occhino, Jing Chen et Pete Hunt pour une bonne introduction). En outre, beaucoup de critiques contre AngularJS (L'infrastructure Web la plus répandue, basée sur le modèle MVC, connue pour sa liaison de données bidirectionnelle) inclut des arguments contre ce flux de données bidirectionnel. Voir:
Mise à jour 3: Un autre article intéressant qui explique avec précision certains des problèmes évoqués ci-dessus est Déconstruire le flux de ReactJS - Ne pas utiliser MVC avec ReactJS par Mikael Brassman, auteur de RefluxJS (une bibliothèque simple pour une architecture d’application de flux de données unidirectionnelle inspirée par Flux).
Mise à jour 4: Ember.js s'éloigne actuellement de la liaison de données bidirectionnelle et, dans les futures versions, il sera unidirectionnel par défaut. Voir: L'avenir de Ember parler par Stefan Penner du Symposium Embergarten à Toronto le 15 Novembre 2014.
Mise à jour 5: Voir aussi: RFC The Road to Ember 2.0 - discussion intéressante dans la demande d'attraction de Tom Dale :
"Lorsque nous avons conçu la couche de gabarit d'origine, nous avons pensé qu'il n'était pas très dommageable de rendre toutes les liaisons de données bidirectionnelles: si vous ne définissez pas de liaison bidirectionnelle, c'est une liaison unidirectionnelle de facto!
Nous avons depuis réalisé (avec l'aide de nos amis de React), que les composants veulent pouvoir transmettre des données à leurs enfants sans être obligés de rester vigilants face à des mutations égarées.
De plus, la communication entre les composants est souvent exprimée de la manière la plus naturelle sous forme d'événements ou de rappels . C'est possible dans Ember, mais la prédominance des liaisons de données bidirectionnelles amène souvent les gens à utiliser des liaisons bidirectionnelles comme canal de communication . Les développeurs Ember expérimentés ne commettent pas (en général) cette erreur, mais c’est une erreur facile à commettre. " [Soulignement ajouté]
Native vs. VM
Prise en charge du navigateur natif (lire "garanti pour être plus rapide")
Maintenant enfin quelque chose qui n’est pas une question d’opinion.
En fait, ici, c’est exactement l’inverse. Bien sûr, le code "natif" peut être écrit en C ++, mais selon vous, en quoi les moteurs JavaScript sont-ils écrits?
En fait, les moteurs JavaScript sont vraiment étonnants dans les optimisations qu’ils utilisent aujourd’hui - et non plus seulement en V8, mais SpiderMonkey et même Chakra brillent de nos jours. Et gardez à l'esprit qu'avec les compilateurs JIT, le code est non seulement aussi natif que possible, mais il existe également des opportunités d'optimisation de l'exécution qui sont tout simplement impossibles à exploiter dans tout code compilé de manière statique.
Quand les gens pensent que JavaScript est lent, ils veulent généralement dire que JavaScript accède au DOM. Le DOM est lent. Il est natif, écrit en C ++ et pourtant, il est très lent à cause de la complexité qu’il doit implémenter.
Ouvrez votre console et écrivez:
console.dir(document.createElement('div'));
et voyez combien de propriétés un div
élément vide qui n'est même pas attaché au DOM doit implémenter. Ce ne sont que les propriétés de premier niveau qui sont des "propriétés propres" c'est-à-dire. non hérité de la chaîne de prototypes:
en ligne de la musique en ligne, en mémoire, en mémoire, en charge, en charge, en charge, en charge oncontextmenu, onclose, onclick, onchange, oncanplaythrough, oncanplay, oncancel, onblur, onabort, vérification orthographique, isContentEditable, contentEditable, outerText, innerText, accessKey, caché, webkitdropzone, draggable, dialogable,firstElementChild, enfants, nextElementSibling, précédent, dernier article, système, onwebkitfullscreen, est conçu pour le stockage des documents, contenant des disques, des disques, des disques, des disques et des cartouches clientHeight, clientWidth, clientTop, clientLeft, offsetParent, offsetHeight, offsetWidth, offsetTop, offsetLeft, localName, prefix, namespaceURI, id, style, attributs, tagName, parentElement, textContent, baseURI, propriétaireDocument, nextSibling, nextSibling, previousSibling, précédent parentNode, nodeType, nodeValue, nodeNameoncopy, onbeforepaste, onbeforecut, onbeforecopy, webkitShadowRoot, ensemble de données, classList, className, outerHTML, innerHTML, scrollHight, scrollHidth, scrollTop, scrollLeft, clientHeight, clientWop, clientTop, clientTop, client namespaceURI, id, style, attributs, tagName, parentElement, textContent, baseURI, ownerDocument, nextSibling, previousSibling, lastChild, firstChild, childNodes, parentNode, nodeType, nodeValue, nodeNameoncopy, onbeforepaste, onbeforecut, onbeforecopy, webkitShadowRoot, ensemble de données, classList, className, outerHTML, innerHTML, scrollHight, scrollHidth, scrollTop, scrollLeft, clientHeight, clientWop, clientTop, clientTop, client namespaceURI, id, style, attributs, tagName, parentElement, textContent, baseURI, ownerDocument, nextSibling, previousSibling, lastChild, firstChild, childNodes, parentNode, nodeType, nodeValue, nodeNameparentElement, textContent, baseURI, ownerDocument, nextSibling, previousSibling, lastChild, firstChild, childNodes, parentNode, nodeType, nodeValue, nodeNameparentElement, textContent, baseURI, ownerDocument, nextSibling, previousSibling, lastChild, firstChild, childNodes, parentNode, nodeType, nodeValue, nodeName
Nombre d’entre eux sont en fait des objets imbriqués - pour voir les propriétés de second niveau (propres) d’un natif vide div
dans votre navigateur, voyez ce violon .
Je veux dire sérieusement, la propriété onvolumechange sur chaque nœud div? Est-ce une erreur? Nope, il s’agit simplement d’une ancienne version du modèle d’événement traditionnel DOM de niveau 0 héritée de l’un des gestionnaires d’événements "qui doit être prise en charge par tous les éléments HTML , en tant qu’attributs de contenu et attributs IDL" dans la section 6.1.6.2 de la spécification HTML. par W3C - aucun moyen de le contourner.
En attendant, ce sont les propriétés de premier niveau d'un faux DOM div
dans React:
props, _owner, _lifeCycleState, _pendingProps, _pendingCallbacks, _pendingOwner
C'est une différence, n'est-ce pas? En fait, il s’agit de l’objet entier sérialisé en JSON ( LIVE DEMO ), car il est possible de le sérialiser en JSON car il ne contient aucune référence circulaire - ce qui est impensable dans le monde des DOM natifs ( où il ne ferait qu’une exception ):
{
"props": {},
"_owner": null,
"_lifeCycleState": "UNMOUNTED",
"_pendingProps": null,
"_pendingCallbacks": null,
"_pendingOwner": null
}
C’est à peu près la raison principale pour laquelle React peut être plus rapide que le navigateur natif DOM - car il n’a pas à implémenter ce désordre .
Regardez cette présentation de Steven Luscher pour voir ce qui est plus rapide: un DOM natif écrit en C ++ ou un faux DOM entièrement écrit en JavaScript. C'est une présentation très juste et amusante.
Mise à jour: Ember.js, dans les versions futures, utilisera un DOM virtuel fortement inspiré de React pour améliorer les performances. Voir: L'avenir de Ember parler par Stefan Penner du Symposium Embergarten à Toronto le 15 Novembre 2014.
Pour résumer: les fonctionnalités des composants Web telles que les modèles, la liaison de données ou les éléments personnalisés présenteront de nombreux avantages par rapport à React, mais tant que le modèle d'objet de document ne sera pas considérablement simplifié, les performances n'en feront pas partie.
Mise à jour
Deux mois après avoir posté ces réponses, certaines nouvelles étaient pertinentes ici. Comme je viens de l'écrire sur Twitter , la dernière version de l' éditeur de texte Atom écrit par GitHub en JavaScript utilise React de Facebook pour obtenir de meilleures performances, même si selon Wikipedia "Atom est basé sur Chromium et écrit en C ++", de sorte qu'il contrôle entièrement l'implémentation native du DOM C ++ (voir The Nucleus of Atom ) et est assuré de prendre en charge les composants Web, car il est livré avec son propre navigateur Web. Il s’agit d’un exemple très récent de projet réel qui aurait pu utiliser tout autre type d’optimisation généralement indisponible pour les applications Web. Pourtant, il a choisi d’utiliser React, lui-même écrit en JavaScript, pour obtenir les meilleures performances, même si Atom Pour commencer, React n’a pas été conçu. Ce n’était donc pas un changement anodin.
Mise à jour 2
Todd Parker a fait une comparaison intéressante avec WebPagetest pour comparer les performances des exemples TodoMVC écrits en Angular, Backbone, Ember, Polymer, CanJS, YUI, Knockout, React et Shoestring. C'est la comparaison la plus objective que j'ai vue jusqu'à présent. Ce qui est important ici, c'est que tous les exemples respectifs ont été écrits par des experts dans tous ces cadres. Ils sont tous disponibles sur GitHub et peuvent être améliorés par quiconque pense que certains codes pourraient être optimisés pour être exécutés plus rapidement.
Mise à jour 3
Dans les versions futures, Ember.js inclura un certain nombre de fonctionnalités de React dont il est question ici (y compris un lien virtuel DOM et une liaison de données unidirectionnelle, par exemple), ce qui signifie que les idées issues de React sont déjà en train de migrer vers d'autres frameworks. Voir: RFC The Road to Ember 2.0 - discussion intéressante dans la demande d'attraction de Tom Dale (Date de début: 2014-12-03): "Dans Ember 2.0, nous adopterons un modèle de" DOM virtuel "et de flux de données qui englobe le meilleures idées de React et simplifie la communication entre les composants. "
De plus, Angular.js 2.0 implémente de nombreux concepts présentés ici.
Mise à jour 4
Je dois élaborer sur quelques points pour répondre à ce commentaire d'Igwe Kalu:
"il n’est pas judicieux de comparer React (JSX ou la sortie de la compilation) au code JavaScript pur, lorsque React se réduit finalement à un code JavaScript pur. [...] Quelle que soit la stratégie utilisée par React pour l’insertion DOM, elle peut être appliquée sans utiliser React. Cela dit, n’ajoute aucun avantage particulier à l’examen de la fonctionnalité en question, à part sa commodité. " (commentaire complet ici )
Au cas où ce ne serait pas assez clair, dans une partie de ma réponse, je comparais les performances d'exploitation directement sur le DOM natif (implémenté en tant qu'objets hôte dans le navigateur) par rapport au faux DOM / virtuel de React (implémenté en JavaScript). Le point que je tentais de faire est que le DOM virtuel mis en œuvre en JavaScript peut surperformer le réel DOM implémenté en C ++ et pas que React peut surpasser JavaScript ( ce qui serait évidemment pas beaucoup de sens car il est écrit en JavaScript). Mon argument était que le code C ++ "natif" n'est pas toujours plus rapide que le code JavaScript "non natif". L’utilisation de React pour illustrer ce point n’est qu’un exemple.
Mais ce commentaire a touché une question intéressante. En un sens, il est vrai que vous n’avez besoin d’aucun framework (React, Angular ou jQuery) pour quelque raison que ce soit (comme les performances, la portabilité, les fonctionnalités) car vous pouvez toujours recréer ce que le framework fait pour vous et réinventer la roue - si vous pouvez justifier le coût, c'est-à-dire.
Mais - comme Dave Smith a mis bien dans Comment rater le point lorsque l'on compare les performances du framework web : « Lorsque l'on compare deux frameworks web, la question ne peut mon application être rapide avec le cadre X. La question est sera mon application est rapide avec cadre X."
Dans ma réponse de 2011 à: Quelles sont certaines des raisons techniques empiriques de ne pas utiliser jQuery? J'explique un problème similaire: il n'est pas impossible d'écrire du code de manipulation DOM portable sans une bibliothèque comme jQuery, mais les utilisateurs le font rarement.
Lorsqu'ils utilisent des langages de programmation, des bibliothèques ou des frameworks, les utilisateurs ont tendance à utiliser les méthodes les plus commodes ou les plus idiomatiques, et non les méthodes parfaites mais peu pratiques. La valeur réelle de bons frameworks facilite ce qui serait autrement difficile à faire - et le secret est de rendre les bonnes choses commodes. Le résultat est toujours doté de la même puissance que la forme la plus simple du lambda calcul ou la machine de Turing la plus primitive, mais l’expressivité relative de certains concepts signifie que ces mêmes concepts ont tendance à s’exprimer plus facilement ou plus du tout, et que les bonnes solutions ne sont pas seulement possibles, elles sont également largement mises en œuvre.
Mise à jour 5
Réagir + Performance =? L'article de Paul Lewis de juillet 2015 montre un exemple où React est plus lent que le code JavaScript à la vanille. Il est écrit à la main pour une liste infinie d'images Flickr, ce qui est particulièrement important pour les mobiles. Cet exemple montre que tout le monde devrait toujours tester les performances pour un cas d'utilisation spécifique et pour des plates-formes et des périphériques cibles spécifiques.
Merci à Kevin Lozandier de l' avoir porté à mon attention .