Atualização: esta resposta parece ser bastante popular, então levei um tempo para limpá-la um pouco, adicionar novas informações e esclarecer algumas coisas que eu achava que não eram suficientemente claras. Comente se você acha que algo mais precisa de esclarecimentos ou atualizações.
A maioria das suas preocupações é realmente uma questão de opinião e preferência pessoal, mas tentarei responder o mais objetivamente possível:
Nativo vs. Compilado
Escreva JavaScript em baunilha JavaScript, escreva CSS em CSS, escreva HTML em HTML.
Naquela época, havia muitos debates sobre se alguém deveria escrever um Assembly nativo manualmente ou usar uma linguagem de nível superior como C para fazer o compilador gerar o código do Assembly para você. Mesmo antes disso, as pessoas se recusavam a confiar em montadores e preferiam escrever código de máquina nativo manualmente ( e não estou brincando ).
Atualmente, muitas pessoas escrevem HTML em Haml ou Jade , CSS em Sass ou Less e JavaScript em CoffeeScript ou TypeScript . Está lá. Funciona. Algumas pessoas preferem, outras não.
O ponto é que não há nada de fundamentalmente errado em não escrever JavaScript em vanilla JavaScript, CSS em CSS e HTML em HTML. É realmente uma questão de preferência.
DSLs internas vs. externas
Em vez disso, o encapsulamento de estilo usando o Shadow DOM React possui isso, o que requer a gravação de CSS em JavaScript. Feio.
Bonito ou não, é certamente expressivo. JavaScript é uma linguagem muito poderosa, muito mais poderosa que CSS (mesmo incluindo qualquer um dos pré-processadores CSS). Depende de você preferir DSLs internas ou externas para esse tipo de coisa. Novamente, uma questão de preferência.
(Observação: eu estava falando sobre os estilos embutidos no React que foram mencionados na pergunta original.)
Tipos de DSLs - explicação
Atualização: Lendo minha resposta algum tempo depois de escrevê-la, acho que preciso explicar o que quero dizer aqui. DSL é uma linguagem específica do domínio e pode ser interna (usando a sintaxe da linguagem do host como JavaScript - como, por exemplo, React sem JSX, ou como os estilos embutidos em React mencionados acima) ou pode ser externa (usando uma sintaxe diferente que a linguagem host - como neste exemplo, seria CSS embutido (um DSL externo) dentro do JavaScript).
Pode ser confuso, porque algumas literaturas usam termos diferentes de "interno" e "externo" para descrever esses tipos de DSLs. Às vezes, "incorporado" é usado em vez de "interno", mas a palavra "incorporado" pode significar coisas diferentes - por exemplo, Lua é descrita como "Lua: uma linguagem incorporada extensível", onde incorporado não tem nada a ver com DSL (interno) incorporado (em nesse sentido, é exatamente o oposto - um DSL externo), mas significa que ele está incorporado no mesmo sentido que, digamos, o SQLite é um banco de dados incorporado. Existe até o eLua onde "e" significa "incorporado" em um terceiro sentido - que é destinado a sistemas embarcados! É por isso que não gosto de usar o termo "DSL incorporado" porque coisas como eLua podem ser "DSLs" que são "incorporadas" em dois sentidos diferentes, embora não sejam uma "DSL incorporada"!
Para piorar as coisas, alguns projetos introduzem ainda mais confusão na mistura. Por exemplo. Os modelos de flatiron são descritos como "livres de DSL", enquanto na verdade é apenas um exemplo perfeito de uma DSL interna com sintaxe como:map.where('href').is('/').insert('newurl');
Dito isto, quando escrevi "O JavaScript é uma linguagem muito poderosa, muito mais poderosa que o CSS (mesmo incluindo qualquer um dos pré-processadores CSS). Depende de se você prefere DSLs internas ou externas para esse tipo de coisa. Novamente, uma questão de preferência ". Eu estava falando sobre esses dois cenários:
1:
/** @jsx React.DOM */
var colored = {
color: myColor
};
React.renderComponent(<div style={colored}>Hello World!</div>, mountNode);
Dois:
// SASS:
.colored {
color: $my-color;
}
// HTML:
<div class="colored">Hello World!</div>
O primeiro exemplo usa o que foi descrito na pergunta como: "escrevendo CSS em JavaScript. Não é bonito". O segundo exemplo usa Sass. Embora eu concorde que o uso do JavaScript para escrever CSS pode não ser bonito (para algumas definições de "bonito"), mas há uma vantagem em fazê-lo.
Posso ter variáveis e funções no Sass, mas elas têm escopo lexicamente ou dinamicamente? Eles são estaticamente ou dinamicamente digitados? Forte ou fraca? E os tipos numéricos? Digite coerência? Quais valores são verdadeiros e quais são falsos? Posso ter funções de ordem superior? Recursão? Chamadas de cauda? Fechamentos lexicais? Eles são avaliados em ordem normal ou ordem de aplicação? Existe uma avaliação preguiçosa ou ansiosa? Os argumentos para funções são passados por valor ou por referência? Eles são mutáveis? Imutável? Persistente? E os objetos? Aulas? Protótipos? Herança?
Essas não são perguntas triviais e, no entanto, preciso saber as respostas para entender o código Sass ou Less. Eu já conheço essas respostas para JavaScript, então isso significa que eu já entendo todas as DSLs internas (como os estilos embutidos no React) nesses níveis. Portanto, se eu usar o React, preciso conhecer apenas um conjunto de respostas para essas (e muitas outras semelhantes). ) perguntas, enquanto quando eu uso para, por exemplo. Sass e guidão, então eu tenho que conhecer três conjuntos dessas respostas e entender suas implicações.
Não é para dizer que de um jeito ou de outro é sempre melhor, mas toda vez que você introduz outro idioma no mix, paga um preço que pode não ser tão óbvio à primeira vista, e esse preço é complexo.
Espero ter esclarecido um pouco o que eu originalmente quis dizer.
Ligação de dados
Encadernação bidirecional
Este é um assunto realmente interessante e, de fato, também uma questão de preferência. A mão dupla nem sempre é melhor que a mão única. É uma questão de como você deseja modelar o estado mutável em seu aplicativo. Eu sempre vi as ligações bidirecionais como uma idéia um pouco contrária aos princípios da programação funcional, mas a programação funcional não é o único paradigma que funciona, algumas pessoas preferem esse tipo de comportamento e as duas abordagens parecem funcionar muito bem na prática. Se você estiver interessado nos detalhes das decisões de design relacionadas à modelagem do estado no React, assista à palestra de Pete Hunt (vinculada à pergunta) e à palestra de Tom Occhino e Jordan Walke que explicam muito bem isso em minha opinião.
Atualização: Veja também outra palestra de Pete Hunt: Seja previsível, não correto: programação DOM funcional .
Atualização 2: Vale a pena notar que muitos desenvolvedores estão argumentando contra o fluxo de dados bidirecional ou ligação bidirecional, alguns até chamam isso de antipadrão. Tomemos, por exemplo, a arquitetura de aplicativos Flux que evita explicitamente o modelo MVC (que provou ser difícil de escalar para grandes aplicativos do Facebook e Instagram) em favor de um fluxo de dados estritamente unidirecional (consulte a conversa sobre Hacker Way: Repensando o Desenvolvimento de Aplicativos Web no Facebook por Tom Occhino, Jing Chen e Pete Hunt para uma boa introdução). Além disso, muitas críticas contra o AngularJS (a estrutura da Web mais popular que é vagamente baseada no modelo MVC, conhecida por ligação de dados bidirecional) inclui argumentos contra esse fluxo de dados bidirecional, consulte:
Atualização 3: Outro artigo interessante que explica bem alguns dos problemas discutidos acima é Desconstruindo o Fluxo do ReactJS - Não usando o MVC com o ReactJS de Mikael Brassman, autor do RefluxJS (uma biblioteca simples para arquitetura de aplicativo de fluxo de dados unidirecional inspirada pelo Flux).
Atualização 4: o Ember.js está atualmente se afastando da ligação de dados bidirecional e, em versões futuras, será unidirecional por padrão. Veja: O Futuro da Ember, por Stefan Penner, do Simpósio Embergarten, em Toronto, em 15 de novembro de 2014.
Atualização 5: Veja também: The Road to Ember 2.0 RFC - discussão interessante na solicitação de recebimento de Tom Dale :
"Quando projetamos a camada de modelo original, descobrimos que fazer todas as ligações de dados bidirecionais não era muito prejudicial: se você não definir uma ligação bidirecional, é de fato uma ligação unidirecional!
Desde então, percebemos (com a ajuda de nossos amigos do React) que os componentes querem poder fornecer dados para seus filhos sem ter que ficar atento a mutações rebeldes.
Além disso, a comunicação entre componentes geralmente é expressa naturalmente como eventos ou retornos de chamada . Isso é possível no Ember, mas o domínio das ligações de dados bidirecionais geralmente leva as pessoas a seguir o caminho de usar as ligações bidirecionais como um canal de comunicação . Desenvolvedores experientes de Ember (geralmente) não cometem esse erro, mas é fácil de cometer ". [Ênfase adicionada]
Nativo x VM
Suporte nativo ao navegador (leia "garantia de ser mais rápido")
Agora, finalmente, algo que não é uma questão de opinião.
Na verdade, aqui é exatamente o contrário. É claro que o código "nativo" pode ser escrito em C ++, mas no que você acha que os mecanismos JavaScript estão escritos?
De fato, os mecanismos JavaScript são realmente incríveis nas otimizações que eles usam hoje - e não apenas no V8, mas também no SpiderMonkey e até no Chakra. E lembre-se de que, com os compiladores JIT, o código não é apenas o mais nativo possível, mas também existem oportunidades de otimização do tempo de execução que são simplesmente impossíveis de serem executadas em qualquer código compilado estaticamente.
Quando as pessoas pensam que o JavaScript é lento, geralmente significam JavaScript que acessa o DOM. O DOM está lento. É nativo, escrito em C ++ e, no entanto, é lento como o inferno por causa da complexidade que precisa implementar.
Abra seu console e escreva:
console.dir(document.createElement('div'));
e veja quantas propriedades um div
elemento vazio que nem mesmo está anexado ao DOM deve implementar. Estas são apenas as propriedades de primeiro nível que são "propriedades próprias", ou seja. não herdado da cadeia de protótipos:
alinhar, aguardar, onvolumechange, ontimeupdate, onsuspend, onsubmit, onstalled, onshow, onselect, onseeking, onseeked, onscroll, onresize, onreset, onratechange, onprogress, onplaying, onplay, onpause, onmousewheel, onmousemovermousemover onmouseenter, onmousedown, onloadstart, onloadedmetadata, onloadeddata, onload, onkeyup, onkeypress, onkeydown, oninvalid, oninput, onfocus, onerror, onended, onemptied, ondurationchange, ondrop, ondragstart, ondragover, ondragluve, ondrenter, ondrag, onbl oncontextmenu, onclose, onclick, onchange, oncanplaythrough, oncanplay, oncancel, onblur, onabort, spellcheck, isContentEditable, contentEditable, outerText, innerText, accessKey, hidden, webkitdropzone, draggable, tabIndex, dir, translateE, lang, title, child, lang, titlefirstElementChild, children, nextElementSibling, previousElementSibling, onwheel, onwebkitfullscreenerror, onwebkitfullscreenchange, onselectstart, onsearch, onpaste, oncut, oncopy, onbeforepaste, onbeforecut, onbeforecopy, webkitShadow, clientHeight, clientWidth, clientTop, clientLeft, offsetParent, offsetHeight, offsetWidth, offsetTop, offsetLeft, localName, prefixo, namespaceURI, id, estilo, atributos, tagName, parentElement, textContent, baseURI, ownerDocument, nextSibling, previousSibling, lastNild, firstChild, firstChild parentNode, nodeType, nodeValue, nodeNameoncopy, onbeforepaste, onbeforecut, onbeforecopy, webkitShadowRoot, conjunto de dados, classList, className, outerHTML, innerHTML, scrollHeight, scrollWidth, scrollTop, scrollLeft, clientHeight, clientWidth, clientTop, clientLeft, offsetParent, offsetHeight, offsetWidight, offsetWid, namespaceURI, id, estilo, atributos, tagName, parentElement, textContent, baseURI, ownerDocument, nextSibling, previousSibling, lastChild, firstChild, childNodes, parentNode, nodeType, nodeValue, nodeNameoncopy, onbeforepaste, onbeforecut, onbeforecopy, webkitShadowRoot, conjunto de dados, classList, className, outerHTML, innerHTML, scrollHeight, scrollWidth, scrollTop, scrollLeft, clientHeight, clientWidth, clientTop, clientLeft, offsetParent, offsetHeight, offsetWidight, offsetWid, 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
Muitos deles são realmente objetos aninhados - para ver propriedades de segundo nível (próprias) de um nativo vazio div
em seu navegador, consulte este violino .
Quero dizer a sério, a propriedade onvolumechange em cada nó div? Isso é um erro? Não, é apenas uma versão herdada do modelo de evento tradicional DOM Nível 0 de um dos manipuladores de eventos "que deve ser suportada por todos os elementos HTML , como atributos de conteúdo e IDL" [ênfase adicionada] na Seção 6.1.6.2 da especificação HTML por W3C - de jeito nenhum.
Enquanto isso, estas são as propriedades de primeiro nível de um DOM falso div
no React:
props, _owner, _lifeCycleState, _pendingProps, _pendingCallbacks, _pendingOwner
Quanta diferença, não é? Na verdade, este é o objeto inteiro serializado para JSON ( LIVE DEMO ), porque, na verdade, você pode serializá-lo para JSON, pois ele não contém nenhuma referência circular - algo impensável no mundo do DOM nativo ( onde seria apenas uma exceção ):
{
"props": {},
"_owner": null,
"_lifeCycleState": "UNMOUNTED",
"_pendingProps": null,
"_pendingCallbacks": null,
"_pendingOwner": null
}
Essa é a principal razão pela qual o React pode ser mais rápido que o DOM do navegador nativo - porque ele não precisa implementar essa bagunça .
Veja esta apresentação de Steven Luscher para ver o que é mais rápido: DOM nativo escrito em C ++ ou um DOM falso escrito inteiramente em JavaScript. É uma apresentação muito justa e divertida.
Atualização: o Ember.js em versões futuras usará um DOM virtual fortemente inspirado no React para melhorar o desempenho. Veja: O Futuro da Ember, por Stefan Penner, do Simpósio Embergarten, em Toronto, em 15 de novembro de 2014.
Para resumir: os recursos dos Componentes da Web, como modelos, ligação de dados ou elementos personalizados, terão muitas vantagens sobre o React, mas até o próprio modelo de objeto de documento ser significativamente simplificado, o desempenho não será um deles.
Atualizar
Dois meses depois de postar essas respostas, houve algumas notícias relevantes aqui. Como acabei de escrever no Twitter , a versão mais recente do editor de texto Atom , escrita pelo GitHub em JavaScript, usa o React do Facebook para obter um melhor desempenho, embora de acordo com a Wikipedia "O Atom seja baseado no Chromium e esteja escrito em C ++", para ter controle total dos a implementação nativa do C ++ DOM (consulte The Nucleus of Atom ) e é garantido que tenha suporte para Web Components, uma vez que é fornecido com seu próprio navegador. É apenas um exemplo muito recente de um projeto do mundo real que poderia ter usado qualquer outro tipo de otimização normalmente indisponível para aplicativos da Web e, no entanto, optou por usar o React, que está escrito em JavaScript, para obter o melhor desempenho, mesmo que o Atom não foi construído com o React, portanto, não foi uma mudança trivial.
Atualização 2
Existe uma comparação interessante de Todd Parker usando o WebPagetest para comparar o desempenho dos exemplos TodoMVC escritos em Angular, Backbone, Ember, Polymer, CanJS, YUI, Knockout, React e Shoestring. Esta é a comparação mais objetiva que eu já vi até agora. O que é significativo aqui é que todos os exemplos respectivos foram escritos por especialistas em todas essas estruturas, estão todos disponíveis no GitHub e podem ser aprimorados por qualquer pessoa que pense que parte do código pode ser otimizado para executar mais rapidamente.
Atualização 3
O Ember.js em versões futuras incluirá vários recursos do React discutidos aqui (incluindo um DOM virtual e uma ligação de dados unidirecional, para citar apenas alguns), o que significa que as idéias originadas no React já estão migrando para outras estruturas. Veja: The Road to Ember 2.0 RFC - discussão interessante na solicitação pull de Tom Dale (Data de início: 2014-12-03): "No Ember 2.0, adotaremos um" DOM virtual "e um modelo de fluxo de dados que abraça o melhores idéias do React e simplifica a comunicação entre os componentes ".
Além disso, o Angular.js 2.0 está implementando muitos dos conceitos discutidos aqui.
Atualização 4
Eu tenho que elaborar algumas questões para responder a este comentário de Igwe Kalu:
"não é sensato comparar o React (JSX ou a saída de compilação) com o JavaScript comum, quando o React acaba por reduzir ao JavaScript comum. [...] Qualquer que seja a estratégia usada pelo React para a inserção do DOM, pode ser aplicada sem o uso do React. Dito isso, ele não adiciona nenhum benefício especial ao considerar o recurso em questão que não seja a conveniência ". (comentário completo aqui )
Caso não tenha ficado claro o suficiente, em parte da minha resposta, estou comparando o desempenho de operar diretamente no DOM nativo (implementado como objetos de host no navegador) versus o DOM falso / virtual do React (implementado em JavaScript). O ponto que eu estava tentando destacar é que o DOM virtual implementado em JavaScript pode superar o DOM real implementado em C ++ e não que o React possa superar o JavaScript (o que obviamente não faria muito sentido, pois está escrito em JavaScript). Meu argumento foi que nem sempre é garantido que o código C ++ "nativo" seja mais rápido que o JavaScript "não nativo". Usar o React para ilustrar esse ponto foi apenas um exemplo.
Mas esse comentário tocou uma questão interessante. Em certo sentido, é verdade que você não precisa de nenhuma estrutura (React, Angular ou jQuery) por qualquer motivo (como desempenho, portabilidade, recursos), porque você sempre pode recriar o que a estrutura faz por você e reinventar a roda - se você pode justificar o custo, é isso.
Mas - como Dave Smith muito bem colocá-lo em Como perder o ponto quando se compara o desempenho framework web : "Ao comparar dois frameworks web, a questão não é possível meu aplicativo ser rápido com o quadro X. A questão é que meu aplicativo ser rápido com o quadro X. "
Na minha resposta de 2011 para: Quais são algumas razões técnicas empíricas para não usar o jQuery , explico um problema semelhante: não é impossível escrever código portátil de manipulação de DOM sem uma biblioteca como o jQuery, mas as pessoas raramente o fazem.
Ao usar linguagens de programação, bibliotecas ou estruturas, as pessoas tendem a usar as maneiras mais convenientes ou idiomáticas de fazer as coisas, não as perfeitas, mas as inconvenientes. O verdadeiro valor de boas estruturas é facilitar o que seria difícil de fazer - e o segredo é tornar as coisas certas convenientes. O resultado ainda tem exatamente o mesmo poder à sua disposição que a forma mais simples de cálculo lambda ou a máquina de Turing mais primitiva, mas a expressividade relativa de certos conceitos significa que esses mesmos conceitos tendem a se expressar com mais facilidade ou facilidade, e que as soluções certas não são apenas possíveis, mas realmente implementadas amplamente.
Atualização 5
Reagir + Desempenho =? O artigo de Paul Lewis, de julho de 2015, mostra um exemplo em que o React é mais lento que o JavaScript baunilha, escrito à mão para uma lista infinita de fotos do Flickr, o que é especialmente significativo no celular. Este exemplo mostra que todos devem sempre testar o desempenho para casos de uso específicos e plataformas e dispositivos de destino específicos.
Agradeço a Kevin Lozandier por chamar minha atenção .