Actualización: esta respuesta parece ser bastante popular, así que me tomé un tiempo para limpiarla un poco, agregar información nueva y aclarar algunas cosas que pensé que no estaban lo suficientemente claras. Comente si cree que algo más necesita aclaraciones o actualizaciones.
La mayoría de sus preocupaciones son realmente una cuestión de opinión y preferencia personal, pero trataré de responder de la manera más objetiva posible:
Nativo vs. Compilado
Escriba JavaScript en JavaScript vainilla, escriba CSS en CSS, escriba HTML en HTML.
En el pasado hubo debates candentes sobre si uno debería escribir ensamblado nativo a mano o usar un lenguaje de nivel superior como C para hacer que el compilador genere código de ensamblado para usted. Incluso antes de eso, la gente se negaba a confiar en los ensambladores y prefería escribir código máquina nativo a mano ( y no estoy bromeando ).
Mientras tanto, hoy en día hay muchas personas que escriben HTML en Haml o Jade , CSS en Sass o Less y JavaScript en CoffeeScript o TypeScript . Está allá. Funciona. Algunas personas lo prefieren, otras no.
El punto es que no hay nada fundamentalmente incorrecto en no escribir JavaScript en JavaScript vainilla, CSS en CSS y HTML en HTML. Es realmente una cuestión de preferencia.
DSL internos versus externos
En cambio, la encapsulación de estilo con Shadow DOM React tiene esto, que requiere escribir CSS en JavaScript. No es bonito.
Bonita o no, sin duda es expresiva. JavaScript es un lenguaje muy poderoso, mucho más poderoso que CSS (incluso incluyendo cualquiera de los preprocesadores CSS). Depende de si prefiere DSL internas o externas para ese tipo de cosas. De nuevo, una cuestión de preferencia.
(Nota: estaba hablando de los estilos en línea en React a los que se hizo referencia en la pregunta original).
Tipos de DSL - explicación
Actualización: leyendo mi respuesta un tiempo después de escribirla, creo que necesito explicar lo que quiero decir aquí. DSL es un lenguaje específico de dominio y puede ser interno (usando la sintaxis del lenguaje anfitrión como JavaScript, como por ejemplo React sin JSX, o como los estilos en línea en React mencionados anteriormente) o puede ser externo (usando una sintaxis diferente que el idioma del host, como en este ejemplo estaría alineado CSS (un DSL externo) dentro de JavaScript).
Puede ser confuso porque algunas publicaciones usan términos diferentes a "interno" y "externo" para describir ese tipo de DSL. A veces se usa "incrustado" en lugar de "interno", pero la palabra "incrustado" puede significar cosas diferentes, por ejemplo, Lua se describe como "Lua: un lenguaje incrustado extensible" donde incrustado no tiene nada que ver con DSL incrustado (interno) en qué sentido es todo lo contrario: un DSL externo) pero significa que está incrustado en el mismo sentido que, por ejemplo, SQLite es una base de datos incrustada. Incluso hay eLua donde "e" significa "integrado" en un tercer sentido: que está destinado a sistemas integrados! ¡Es por eso que no me gusta usar el término "DSL incrustado" porque cosas como eLua pueden ser "DSL" que están "incrustadas" en dos sentidos diferentes sin ser un "DSL incrustado" en absoluto!
Para empeorar las cosas, algunos proyectos introducen aún más confusión en la mezcla. P.ej. Las plantillas Flatiron se describen como "sin DSL" mientras que, de hecho, es solo un ejemplo perfecto de un DSL interno con una sintaxis como:map.where('href').is('/').insert('newurl');
Dicho esto, cuando escribí "JavaScript es un lenguaje muy poderoso, mucho más poderoso que CSS (incluso incluyendo cualquiera de los preprocesadores CSS). Depende de si prefieres DSLs internos o externos para ese tipo de cosas. Nuevamente, una cuestión de preferencia ". Estaba hablando de esos dos escenarios:
Uno:
/** @jsx React.DOM */
var colored = {
color: myColor
};
React.renderComponent(<div style={colored}>Hello World!</div>, mountNode);
Dos:
// SASS:
.colored {
color: $my-color;
}
// HTML:
<div class="colored">Hello World!</div>
El primer ejemplo utiliza lo que se describió en la pregunta como: "escribir CSS en JavaScript. No es bonito". El segundo ejemplo usa Sass. Si bien estoy de acuerdo en que usar JavaScript para escribir CSS puede no ser bonito (para algunas definiciones de "bonito"), pero hay una ventaja de hacerlo.
Puedo tener variables y funciones en Sass, pero ¿tienen un alcance léxico o un alcance dinámico? ¿Se escriben estática o dinámicamente? ¿Fuerte o débil? ¿Qué pasa con los tipos numéricos? Tipo coersion? ¿Qué valores son verdaderos y cuáles son falsos? ¿Puedo tener funciones de orden superior? Recursividad? ¿Llamadas de cola? Cierres léxicos? ¿Se evalúan en orden normal u orden aplicativo? ¿Hay una evaluación perezosa o ansiosa? ¿Se pasan los argumentos a las funciones por valor o por referencia? ¿Son mutables? ¿Inmutable? ¿Persistente? ¿Qué hay de los objetos? Clases? Prototipos? ¿Herencia?
Esas no son preguntas triviales y, sin embargo, tengo que saber las respuestas a ellas si quiero entender el código Sass o Less. Ya conozco esas respuestas para JavaScript, por lo que significa que ya entiendo cada DSL interno (como los estilos en línea en React) en esos mismos niveles, por lo que si uso React, tengo que saber solo un conjunto de respuestas a esas (y muchas similares ) preguntas, mientras que cuando uso por ej. Sass y Handlebars, entonces tengo que conocer tres conjuntos de esas respuestas y comprender sus implicaciones.
No quiere decir que de una forma u otra siempre es mejor, pero cada vez que introduce otro idioma en la mezcla, paga un precio que puede no ser tan obvio a primera vista, y este precio es complejo.
Espero haber aclarado un poco lo que quise decir originalmente.
El enlace de datos
Enlace bidireccional
Este es un tema realmente interesante y, de hecho, también es una cuestión de preferencia. Dos vías no siempre es mejor que una. Se trata de cómo desea modelar el estado mutable en su aplicación. Siempre vi los enlaces bidireccionales como una idea algo contraria a los principios de la programación funcional, pero la programación funcional no es el único paradigma que funciona, algunas personas prefieren este tipo de comportamiento y ambos enfoques parecen funcionar bastante bien en la práctica. Si está interesado en los detalles de las decisiones de diseño relacionadas con el modelado del estado en React, mire la charla de Pete Hunt (vinculada a la pregunta) y la charla de Tom Occhino y Jordan Walke, quienes lo explican muy bien en mi opinión.
Actualización: Vea también otra charla de Pete Hunt: Sea predecible, no correcto: programación DOM funcional .
Actualización 2: Vale la pena señalar que muchos desarrolladores argumentan en contra del flujo de datos bidireccional, o el enlace bidireccional, algunos incluso lo llaman antipatrón. Tomemos por ejemplo el flujo arquitectura de la aplicación que evita explícitamente la MVC modelo (que resultó ser difícil de escalar para aplicaciones de gran Facebook e Instagram) a favor de un flujo de datos estrictamente unidireccional (ver el Camino Hacker: Rethinking Web desarrollo de aplicaciones en Facebook hablar por Tom Occhino, Jing Chen y Pete Hunt para una buena introducción). Además, muchas críticas contra AngularJS (el marco web más popular que se basa libremente en el modelo MVC, conocido por el enlace de datos bidireccional) incluye argumentos en contra de ese flujo de datos bidireccional, consulte:
Actualización 3: Otro artículo interesante que explica muy bien algunos de los problemas discutidos anteriormente es Deconstruir Flux de ReactJS: no usar MVC con ReactJS por Mikael Brassman, autor de RefluxJS (una biblioteca simple para la arquitectura de aplicación de flujo de datos unidireccional inspirada en Flux).
Actualización 4: Ember.js actualmente se está alejando del enlace de datos bidireccional y en versiones futuras será unidireccional por defecto. Ver: El futuro de Ember, charla de Stefan Penner del Simposio Embergarten en Toronto el 15 de noviembre de 2014.
Actualización 5: Vea también: The Road to Ember 2.0 RFC - discusión interesante en la solicitud de extracción de Tom Dale :
"Cuando diseñamos la capa de plantillas original, supusimos que hacer todos los enlaces de datos en dos direcciones no era muy dañino: si no establece un enlace en dos direcciones, ¡es un enlace en una sola dirección!
Desde entonces nos hemos dado cuenta (con la ayuda de nuestros amigos de React), que los componentes quieren poder entregar datos a sus hijos sin tener que estar en guardia para las mutaciones rebeldes.
Además, la comunicación entre componentes a menudo se expresa más naturalmente como eventos o devoluciones de llamada . Esto es posible en Ember, pero el dominio de los enlaces de datos bidireccionales a menudo lleva a las personas por el camino del uso de enlaces bidireccionales como canal de comunicación . Los desarrolladores experimentados de Ember no (generalmente) cometen este error, pero es fácil de cometer ". [Énfasis agregado]
Nativo vs. VM
Soporte de navegador nativo (lea "garantizado para ser más rápido")
Ahora, finalmente, algo que no es una cuestión de opinión.
En realidad, aquí es exactamente al revés. Por supuesto, el código "nativo" se puede escribir en C ++, pero ¿en qué cree que están escritos los motores de JavaScript?
De hecho, los motores de JavaScript son realmente sorprendentes en las optimizaciones que usan hoy en día, y no solo V8, sino también SpiderMonkey e incluso Chakra brilla en estos días. Y tenga en cuenta que con los compiladores JIT, el código no solo es tan nativo como puede ser, sino que también hay oportunidades de optimización del tiempo de ejecución que son simplemente imposibles de hacer en cualquier código compilado estáticamente.
Cuando las personas piensan que JavaScript es lento, generalmente se refieren a JavaScript que accede al DOM. El DOM es lento. Es nativo, escrito en C ++ y, sin embargo, es lento como el infierno debido a la complejidad que tiene que implementar.
Abre tu consola y escribe:
console.dir(document.createElement('div'));
y vea cuántas propiedades div
debe implementar un elemento vacío que ni siquiera está conectado al DOM. Estas son solo las propiedades de primer nivel que son "propiedades propias", es decir. no heredado de la cadena prototipo:
alinear, en espera, en cambio de volumen, en actualización de tiempo, en suspensión, en presentación, en instalación, en demostración, en selección, en búsqueda, en buscado, en exploración, en tamaño, en reposo, en tasa de cambio, en progreso, en reproducción, en reproducción, en pausa, en rueda de ratón, en ratón, en ratón, en el ratón, en el ratón, en el ratón, en el ratón, en el ratón, en el ratón, en el ratón, en el ratón, en el ratón onmouseenter, onmousedown, onloadstart, onloadedmetadata, onloadeddata, onload, onkeyup, onkeypress, onkeydown, oninvalid, oninput, onfocus, onerror, onended, onemptied, ondurationchange, ondrop, ondragstart, ondragover, ondragleave, ondrage, ondrage, ondrage, ondrage, ondrage. oncontextmenu, onclose, onclick, onchange, oncanplaythrough, oncanplay, oncancel, onblur, onabort, spellcheck, isContentEditable, contentEditable, outsideText, innerText, accessKey, hidden, webkitdropzone, draggable, tabIndex, dir, translate, lang, title, childElementCoofirstElementChild, children, nextElementSibling, anteriorElementSibling, onwheel, onwebkitfullscreenerror, onwebkitfullscreenchange, onselectstart, onsearch, onpaste, oncut, oncopy, onbeforepaste, onbeforepaste,. clientHeight, clientWidth, clientTop, clientLeft, offsetParent, offsetHeight, offsetWidth, offsetTop, offsetLeft, localName, prefix, namespaceURI, id, style, atributos, tagName, parentElement, textContent, baseURI, ownerDocument, nextSibling, previousSibling, lastChild, firstChild, firstChild, firstChild, firstChild, firstChild, firstChild, firstChild parentNode, nodeType, nodeValue, nodeNameoncopy, onbeforepaste, onbeforecut, onbeforecopy, webkitShadowRoot, conjunto de datos, classList, className, externalHTML, innerHTML, scrollHeight, scrollWidth, scrollTop, scrollLeft, clientHeight, clientWidth, clientTop, clientLeft, offsetParent, offsetHeight, offsetWidth, offset, offset, namespaceURI, id, estilo, atributos, tagName, parentElement, textContent, baseURI, ownerDocument, nextSibling, previousSibling, lastChild, firstChild, childNodes, parentNode, nodeType, nodeValue, nodeNameoncopy, onbeforepaste, onbeforecut, onbeforecopy, webkitShadowRoot, conjunto de datos, classList, className, externalHTML, innerHTML, scrollHeight, scrollWidth, scrollTop, scrollLeft, clientHeight, clientWidth, clientTop, clientLeft, offsetParent, offsetHeight, offsetWidth, offset, offset, namespaceURI, id, estilo, atributos, 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
Muchos de ellos son en realidad objetos anidados: para ver las propiedades de segundo nivel (propias) de un nativo vacío div
en su navegador, consulte este violín .
Quiero decir en serio, ¿ propiedad onvolumechange en cada nodo div? ¿Es un error? No, es solo una versión de modelo de evento tradicional DOM Nivel 0 heredado de uno de los controladores de eventos "que deben ser compatibles con todos los elementos HTML , como atributos de contenido y atributos IDL" [énfasis agregado] en la Sección 6.1.6.2 de la especificación HTML por W3C - no hay forma de evitarlo.
Mientras tanto, estas son las propiedades de primer nivel de un falso DOM div
en React:
accesorios, _owner, _lifeCycleState, _pendingProps, _pendingCallbacks, _pendingOwner
Toda una diferencia, ¿no? De hecho, este es el objeto completo serializado a JSON ( DEMO EN VIVO ), porque bueno, en realidad puedes serializarlo a JSON ya que no contiene referencias circulares, algo impensable en el mundo del DOM nativo ( donde solo arrojaría una excepción ):
{
"props": {},
"_owner": null,
"_lifeCycleState": "UNMOUNTED",
"_pendingProps": null,
"_pendingCallbacks": null,
"_pendingOwner": null
}
Esta es la razón principal por la que React puede ser más rápido que el DOM nativo del navegador, ya que no tiene que implementar este desastre .
Vea esta presentación de Steven Luscher para ver qué es más rápido: DOM nativo escrito en C ++ o un DOM falso escrito completamente en JavaScript. Es una presentación muy justa y entretenida.
Actualización: Ember.js en futuras versiones utilizará un DOM virtual fuertemente inspirado por React para mejorar el rendimiento. Ver: El futuro de Ember, charla de Stefan Penner del Simposio Embergarten en Toronto el 15 de noviembre de 2014.
En resumen: las características de los componentes web como plantillas, enlace de datos o elementos personalizados tendrán muchas ventajas sobre React, pero hasta que el modelo de objeto del documento se simplifique significativamente, el rendimiento no será una de ellas.
Actualizar
Dos meses después de que publiqué estas respuestas, hubo algunas noticias relevantes aquí. Como acabo de escribir en Twitter , la última versión del editor de texto Atom escrito por GitHub en JavaScript utiliza React de Facebook para obtener un mejor rendimiento, aunque según Wikipedia "Atom se basa en Chromium y está escrito en C ++", por lo que tiene control total de la implementación nativa de C ++ DOM (ver The Nucleus of Atom ) y está garantizado para tener soporte para componentes web, ya que se envía con su propio navegador web. Es solo un ejemplo muy reciente de un proyecto del mundo real que podría haber utilizado cualquier otro tipo de optimización que normalmente no está disponible para aplicaciones web y, sin embargo, ha elegido utilizar React, que está escrito en JavaScript, para lograr el mejor rendimiento, aunque Atom para empezar no se creó con React, por lo que hacerlo no fue un cambio trivial.
Actualización 2
Hay una comparación interesante de Todd Parker usando WebPagetest para comparar el rendimiento de los ejemplos de TodoMVC escritos en Angular, Backbone, Ember, Polymer, CanJS, YUI, Knockout, React y Shoestring. Esta es la comparación más objetiva que he visto hasta ahora. Lo significativo aquí es que todos los ejemplos respectivos fueron escritos por expertos en todos esos marcos, todos están disponibles en GitHub y pueden ser mejorados por cualquiera que piense que parte del código podría optimizarse para ejecutarse más rápido.
Actualización 3
Ember.js en futuras versiones incluirá una serie de características de React que se analizan aquí (incluido un DOM virtual y enlace de datos unidireccional, por nombrar solo algunas), lo que significa que las ideas que se originaron en React ya están migrando a otros marcos. Ver: The Road to Ember 2.0 RFC: discusión interesante en la solicitud de extracción de Tom Dale (Fecha de inicio: 03/12/2014): "En Ember 2.0, adoptaremos un" DOM virtual "y un modelo de flujo de datos que abarque el mejores ideas de React y simplifica la comunicación entre componentes ".
Además, Angular.js 2.0 está implementando muchos de los conceptos discutidos aquí.
Actualización 4
Tengo que elaborar algunos temas para responder a este comentario de Igwe Kalu:
"no es sensato comparar React (JSX o el resultado de la compilación) con JavaScript simple, cuando React finalmente se reduce a JavaScript simple. [...] Cualquier estrategia que React use para la inserción DOM puede aplicarse sin usar React. Dicho esto, no agrega ningún beneficio especial al considerar la característica en cuestión que no sea la conveniencia ". (comentario completo aquí )
En caso de que no fuera lo suficientemente claro, en parte de mi respuesta, estoy comparando el rendimiento de operar directamente en el DOM nativo (implementado como objetos host en el navegador) versus el DOM falso / virtual de React (implementado en JavaScript). El punto que estaba tratando de hacer es que el DOM virtual implementado en JavaScript puede superar el DOM real implementado en C ++ y no que React puede superar a JavaScript (lo que obviamente no tendría mucho sentido ya que está escrito en JavaScript). Mi punto era que no siempre se garantiza que el código "nativo" de C ++ sea más rápido que el JavaScript "no nativo". Usar React para ilustrar ese punto fue solo un ejemplo.
Pero este comentario tocó un tema interesante. En cierto sentido, es cierto que no necesita ningún marco (React, Angular o jQuery) por ningún motivo (como el rendimiento, la portabilidad, las características) porque siempre puede recrear lo que el marco hace por usted y reinventar la rueda, si puede justificar el costo, eso es.
Pero, como bien dijo Dave Smith en Cómo perder el punto al comparar el rendimiento del marco web : "Al comparar dos marcos web, la pregunta no es si mi aplicación puede ser rápida con el marco X. La pregunta es si mi aplicación será rápida con el marco X."
En mi respuesta de 2011 a: ¿Cuáles son algunas razones técnicas empíricas para no usar jQuery ? Explico un problema similar, que no es imposible escribir código portátil de manipulación de DOM sin una biblioteca como jQuery, pero que la gente rara vez lo hace.
Cuando se usan lenguajes de programación, bibliotecas o marcos, las personas tienden a usar las formas más convenientes o idiomáticas de hacer las cosas, no las perfectas sino las inconvenientes. El verdadero valor de los buenos marcos es facilitar lo que de otro modo sería difícil de hacer, y el secreto es hacer que las cosas correctas sean convenientes. El resultado sigue teniendo exactamente el mismo poder a su disposición que la forma más simple de cálculo lambda o la máquina de Turing más primitiva, pero la relativa expresividad de ciertos conceptos significa que esos mismos conceptos tienden a expresarse más fácilmente o en absoluto, y eso Las soluciones correctas no solo son posibles sino que se implementan ampliamente.
Actualización 5
Reaccionar + Rendimiento =? El artículo de Paul Lewis de julio de 2015 muestra un ejemplo en el que React es más lento que JavaScript de vainilla escrito a mano para obtener una lista infinita de imágenes de Flickr, lo que es especialmente significativo en dispositivos móviles. Este ejemplo muestra que todos deben probar siempre el rendimiento para casos de uso específicos y plataformas y dispositivos de destino específicos.
Gracias a Kevin Lozandier por llamar mi atención .