Aggiornamento: questa risposta sembra essere piuttosto popolare, quindi ho impiegato un po 'di tempo per ripulirla un po', aggiungere alcune nuove informazioni e chiarire alcune cose che pensavo non fossero abbastanza chiare. Per favore, commenta se pensi che qualcos'altro abbia bisogno di chiarimenti o aggiornamenti.
La maggior parte delle tue preoccupazioni sono in realtà una questione di opinioni e preferenze personali, ma cercherò di rispondere nel modo più obiettivo possibile:
Nativo vs. Compilato
Scrivi JavaScript in vanilla JavaScript, scrivi CSS in CSS, scrivi HTML in HTML.
In passato ci sono stati dibattiti caldi sulla possibilità di scrivere manualmente l'Assemblea nativa o utilizzare un linguaggio di livello superiore come C per fare in modo che il compilatore generi il codice dell'Assemblea per te. Ancor prima che la gente si rifiutasse di fidarsi degli assemblatori e preferiva scrivere a mano il codice macchina nativo ( e non sto scherzando ).
Nel frattempo, oggi ci sono molte persone che scrivono HTML in Haml o Jade , CSS in Sass o Less e JavaScript in CoffeeScript o TypeScript . È lì. Funziona. Alcune persone lo preferiscono, altri no.
Il punto è che non c'è nulla di fondamentalmente sbagliato nel non scrivere JavaScript in JavaScript vaniglia, CSS in CSS e HTML in HTML. È davvero una questione di preferenza.
DSL interni vs. esterni
L'incapsulamento di stile con Shadow DOM React ha invece questo, che richiede la scrittura di CSS in JavaScript. Non carino.
Abbastanza o no, è certamente espressivo. JavaScript è un linguaggio molto potente, molto più potente dei CSS (incluso anche uno qualsiasi dei preprocessori CSS). Dipende dal fatto che tu preferisca DSL interne o esterne per questo genere di cose. Ancora una volta, una questione di preferenza.
(Nota: stavo parlando degli stili inline in React a cui faceva riferimento la domanda originale.)
Tipi di DSL - spiegazione
Aggiornamento: leggendo la mia risposta qualche tempo dopo averla scritta, penso di dover spiegare cosa intendo qui. DSL è un linguaggio specifico del dominio e può essere interno (usando la sintassi della lingua host come JavaScript - come ad esempio React senza JSX, o come gli stili inline in React menzionati sopra) o può essere esterno (usando una sintassi diversa rispetto alla lingua host, come in questo esempio sarebbe incorporare CSS (un DSL esterno) all'interno di JavaScript).
Può essere fonte di confusione perché alcune pubblicazioni usano termini diversi rispetto a "interno" ed "esterno" per descrivere quel tipo di DSL. A volte viene utilizzato "incorporato" anziché "interno" ma la parola "incorporato" può significare cose diverse, ad esempio Lua è descritta come "Lua: un linguaggio incorporato estensibile" in cui incorporato non ha nulla a che fare con il DSL incorporato (interno) (in il che significa che è esattamente l'opposto - un DSL esterno) ma significa che è incorporato nello stesso senso che, ad esempio, SQLite è un database incorporato. Esiste anche eLua in cui "e" sta per "incorporato" in un terzo senso - che è pensato per sistemi embedded! Ecco perché non mi piace usare il termine "DSL incorporato" perché cose come eLua possono essere "DSL" che sono "incorporate" in due sensi diversi pur non essendo affatto un "DSL incorporato"!
A peggiorare le cose alcuni progetti introducono ancora più confusione nel mix. Per esempio. I modelli flatiron sono descritti come "privi di DSL" mentre in realtà è solo un perfetto esempio di DSL interno con sintassi come:map.where('href').is('/').insert('newurl');
Detto questo, quando ho scritto "JavaScript è un linguaggio molto potente, molto più potente del CSS (incluso anche uno qualsiasi dei preprocessori CSS). Dipende dal fatto che tu preferisca DSL interne o esterne per questo genere di cose. Ancora una volta, una questione di preferenza ". Stavo parlando di quei due scenari:
Uno:
/** @jsx React.DOM */
var colored = {
color: myColor
};
React.renderComponent(<div style={colored}>Hello World!</div>, mountNode);
Due:
// SASS:
.colored {
color: $my-color;
}
// HTML:
<div class="colored">Hello World!</div>
Il primo esempio usa ciò che è stato descritto nella domanda come: "scrivere CSS in JavaScript. Non carino." Il secondo esempio usa Sass. Mentre sono d'accordo che l'uso di JavaScript per scrivere CSS potrebbe non essere carino (per alcune definizioni di "grazioso"), ma c'è un vantaggio nel farlo.
Posso avere variabili e funzioni in Sass ma sono con ambito lessicale o dinamico? Sono digitati staticamente o dinamicamente? Fortemente o debolmente? E i tipi numerici? Digitare la coerizione? Quali valori sono veri e quali sono falsi? Posso avere funzioni di ordine superiore? Ricorsione? La coda chiama? Chiusure lessicali? Sono valutati in ordine normale o in ordine applicativo? C'è una valutazione pigra o desiderosa? Gli argomenti alle funzioni vengono passati per valore o per riferimento? Sono mutabili? Immutabile? Persistente? E gli oggetti? Classi? Prototipi? Eredità?
Quelle non sono domande banali eppure devo conoscere le risposte se voglio capire il codice Sass o Less. Conosco già quelle risposte per JavaScript, quindi significa che capisco già ogni DSL interno (come gli stili inline in React) su quegli stessi livelli, quindi se uso React allora devo conoscere solo una serie di risposte a quelle (e molte simili ) domande, mentre quando uso ad es. Sass e Manubrio quindi devo conoscere tre serie di quelle risposte e capire le loro implicazioni.
Non vuol dire che in un modo o nell'altro sia sempre meglio, ma ogni volta che si introduce un'altra lingua nel mix, si paga un prezzo che potrebbe non essere così ovvio a prima vista, e questo prezzo è complessità.
Spero di aver chiarito un po 'cosa intendevo inizialmente.
Associazione dati
Rilegatura a due vie
Questo è un argomento davvero interessante e in effetti anche una questione di preferenza. Il doppio senso non è sempre meglio del solo senso. È una domanda su come si desidera modellare lo stato mutabile nella propria applicazione. Ho sempre considerato le associazioni bidirezionali un'idea piuttosto contraria ai principi della programmazione funzionale, ma la programmazione funzionale non è l'unico paradigma che funziona, alcune persone preferiscono questo tipo di comportamento ed entrambi gli approcci sembrano funzionare abbastanza bene nella pratica. Se sei interessato ai dettagli delle decisioni di progettazione relative alla modellistica dello stato in React, guarda il discorso di Pete Hunt (collegato alla domanda) e il discorso di Tom Occhino e Jordan Walke che lo spiegano molto bene in la mia opinione.
Aggiornamento: vedi anche un altro discorso di Pete Hunt: sii prevedibile, non corretto: programmazione DOM funzionale .
Aggiornamento 2: Vale la pena notare che molti sviluppatori stanno discutendo contro il flusso di dati bidirezionale o l'associazione bidirezionale, alcuni addirittura lo definiscono un anti-pattern. Prendiamo ad esempio il Flux architettura applicativa che evita in modo esplicito il MVC modello (che si è rivelata difficile da scalare per grandi Facebook e Instagram applicazioni) a favore di un flusso di dati strettamente unidirezionale (vedere la Via Hacker: Ripensare Web App per lo sviluppo Facebook parlare da Tom Occhino, Jing Chen e Pete Hunt per una buona introduzione). Inoltre, molta critica contro AngularJS (il framework Web più popolare che è vagamente basato sul modello MVC, noto per l'associazione di dati bidirezionale) include argomenti contro quel flusso di dati bidirezionale, vedere:
Aggiornamento 3: Un altro articolo interessante che spiega bene alcuni dei problemi discussi sopra è Decostruire il flusso di ReactJS - Non usare MVC con ReactJS di Mikael Brassman, autore di RefluxJS (una semplice libreria per l'architettura applicativa unidirezionale del flusso di dati ispirata da Flux).
Aggiornamento 4: Ember.js si sta attualmente allontanando dall'associazione dati bidirezionale e nelle versioni future sarà a senso unico per impostazione predefinita. Vedi: The Future of Ember talk di Stefan Penner dell'Embergarten Symposium di Toronto il 15 novembre 2014.
Aggiornamento 5: Vedi anche: The Road to Ember 2.0 RFC - discussione interessante nella richiesta pull di Tom Dale :
"Quando abbiamo progettato il layer di template originale, abbiamo pensato che rendere tutti i binding di dati bidirezionali non fosse molto dannoso: se non si imposta un binding bidirezionale, è di fatto un binding unidirezionale!
Da allora ci siamo resi conto (con l'aiuto dei nostri amici di React) che i componenti vogliono essere in grado di distribuire dati ai propri figli senza dover essere in guardia per mutazioni ribelli.
Inoltre, la comunicazione tra i componenti è spesso espressa in modo più naturale come eventi o callback . Ciò è possibile in Ember, ma il predominio dei binding di dati bidirezionali spesso porta le persone a seguire un percorso di utilizzo dei binding bidirezionali come canale di comunicazione . Gli sviluppatori esperti di Ember non (di solito) commettono questo errore, ma è facile da fare. " [Enfasi aggiunta]
Nativo vs. VM
Supporto browser nativo (leggi "garantito per essere più veloce")
Ora finalmente qualcosa che non è una questione di opinione.
In realtà qui è esattamente il contrario. Naturalmente il codice "nativo" può essere scritto in C ++ ma in che cosa pensi siano scritti i motori JavaScript?
È un dato di fatto che i motori JavaScript sono davvero sorprendenti nelle ottimizzazioni che usano oggi - e non solo più V8, anche SpiderMonkey e persino Chakra brillano in questi giorni. E tieni presente che con i compilatori JIT il codice non è solo nativo come può essere, ma ci sono anche opportunità di ottimizzazione del tempo di esecuzione che sono semplicemente impossibili da fare in qualsiasi codice compilato staticamente.
Quando le persone pensano che JavaScript sia lento, di solito significano JavaScript che accede al DOM. Il DOM è lento. È nativo, scritto in C ++ e tuttavia è lento come l'inferno a causa della complessità che deve implementare.
Apri la tua console e scrivi:
console.dir(document.createElement('div'));
e vedi quante proprietà div
deve implementare un elemento vuoto che non è nemmeno collegato al DOM. Queste sono solo le proprietà di primo livello che sono "proprietà proprie" cioè. non ereditato dalla catena di prototipi:
allineare, in attesa, onvolumechange, ontimeupdate, onsuspend, onsubmit, onstalled, onshow, onselect, onseeking, onseeked, onscroll, onresize, onreset, onratechange, onprogress, onplaying, onplay, onpuse, onmousewheelmomo onmouseup onmouseenter, onmousedown, onloadstart, onloadedmetadata, onloadeddata, onload, onkeyup, onkeypress, onkeydown, oninvalid, oninput, onfocus, onerror, onended, onemptied, ondurationchange, ondrop, ondragstart, ondragover, ondragcend oncontextmenu, onclose, onclick, onchange, oncanplaythrough, oncanplay, oncancel, onblur, onabort, spellcheck, isContentEditable, contentEditable, outerText, innerText, accessKey, hidden, webkitdropzone, trascinabile, tabIndex, dir, translate, last, elementfirstElementChild, children, nextElementSibling, previousElementSibling, onwheel, onwebkitfullscreenerror, onwebkitfullscreenchange, onselectstart, onsearch, onpaste, oncut, oncopy, onbeforepaste, onbeforecut, scrollTermetHotetThetetHot clientHeight, clientWidth, clientTop, clientLeft, offsetParent, offsetHeight, offsetWidth, offsetTop, offsetLeft, localName, prefisso, namespaceURI, id, stile, attributi, tagName, parentElement, textContent, baseURI, ownerDocument, nextSibling, previousSibling, lastChild, lastChild parentNode, nodeType, nodeValue, nodeNameoncopy, onbeforepaste, onbeforecut, onbeforecopy, webkitShadowRoot, dataset, classList, className, outerHTML, innerHTML, scrollHeight, scrollWidth, scrollTop, scrollLeft, clientHeight, clientWidth, clientTop, clientLeft, offsetPop namespaceURI, id, stile, attributi, tagName, parentElement, textContent, baseURI, ownerDocument, nextSibling, previousSibling, lastChild, firstChild, childNodes, parentNode, nodeType, nodeValue, nodeNameoncopy, onbeforepaste, onbeforecut, onbeforecopy, webkitShadowRoot, dataset, classList, className, outerHTML, innerHTML, scrollHeight, scrollWidth, scrollTop, scrollLeft, clientHeight, clientWidth, clientTop, clientLeft, offsetPop namespaceURI, id, stile, attributi, 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
Molti di loro sono in realtà oggetti nidificati - per vedere le proprietà (proprie) di secondo livello di un nativo vuoto div
nel tuo browser, vedi questo violino .
Intendo seriamente, proprietà onvolumechange su ogni singolo nodo div? È un errore? No, è solo una versione tradizionale del modello di eventi DOM livello 0 legacy di uno dei gestori di eventi "che deve essere supportata da tutti gli elementi HTML , in quanto sia gli attributi del contenuto che gli attributi IDL" [enfasi aggiunta] nella Sezione 6.1.6.2 delle specifiche HTML di W3C - nessun modo per aggirarlo.
Nel frattempo, queste sono le proprietà di primo livello di un falso DOM div
in React:
oggetti di scena, _owner, _lifeCycleState, _pendingProps, _pendingCallbacks, _pendingOwner
Piuttosto una differenza, no? In realtà questo è l'intero oggetto serializzato per JSON ( LIVE DEMO ), perché ehi è effettivamente possibile serializzare per JSON in quanto non contiene riferimenti circolari - qualcosa di impensabile nel mondo del DOM nativa ( dove sarebbe solo un'eccezione ):
{
"props": {},
"_owner": null,
"_lifeCycleState": "UNMOUNTED",
"_pendingProps": null,
"_pendingCallbacks": null,
"_pendingOwner": null
}
Questo è praticamente il motivo principale per cui React può essere più veloce del DOM del browser nativo, perché non deve implementare questo pasticcio .
Guarda questa presentazione di Steven Luscher per vedere cosa è più veloce: DOM nativo scritto in C ++ o un DOM falso scritto interamente in JavaScript. È una presentazione molto giusta e divertente.
Aggiornamento: Ember.js nelle versioni future utilizzerà un DOM virtuale fortemente ispirato a React per migliorare le prestazioni. Vedi: The Future of Ember talk di Stefan Penner dell'Embergarten Symposium di Toronto il 15 novembre 2014.
Riassumendo: funzionalità di componenti Web come modelli, associazione di dati o elementi personalizzati avranno molti vantaggi rispetto a React ma fino a quando il modello di oggetti del documento stesso non sarà notevolmente semplificato, le prestazioni non saranno una di queste.
Aggiornare
Due mesi dopo aver pubblicato queste risposte, c'erano alcune novità rilevanti qui. Come ho appena scritto su Twitter , l'ultima versione dell'editor di testo Atom scritto da GitHub in JavaScript utilizza React di Facebook per ottenere prestazioni migliori anche se secondo Wikipedia "Atom è basato su Chromium e scritto in C ++", quindi ha il pieno controllo di l'implementazione DOM C ++ nativa (vedi The Nucleus of Atom ) e è garantito il supporto per i componenti Web poiché viene fornito con il proprio browser Web. È solo un esempio molto recente di un progetto del mondo reale che avrebbe potuto utilizzare qualsiasi altro tipo di ottimizzazione in genere non disponibile per le applicazioni Web e tuttavia ha scelto di utilizzare React che è esso stesso scritto in JavaScript, per ottenere le migliori prestazioni, anche se Atom all'inizio non è stato creato con React, quindi non è stato un cambiamento banale.
Aggiornamento 2
Esiste un interessante confronto di Todd Parker che utilizza WebPagetest per confrontare le prestazioni degli esempi di TodoMVC scritti in Angular, Backbone, Ember, Polymer, CanJS, YUI, Knockout, React e Shoestring. Questo è il confronto più oggettivo che ho visto finora. Ciò che è significativo qui è che tutti i rispettivi esempi sono stati scritti da esperti in tutti quei framework, sono tutti disponibili su GitHub e possono essere migliorati da chiunque pensi che parte del codice possa essere ottimizzato per funzionare più velocemente.
Aggiornamento 3
Ember.js nelle versioni future includerà una serie di funzionalità di React che verranno discusse qui (tra cui un DOM virtuale e l'associazione unidirezionale dei dati, solo per citarne alcuni), il che significa che le idee originate in React stanno già migrando in altri framework. Vedi: The Road to Ember 2.0 RFC - interessante discussione nella richiesta pull di Tom Dale (Data di inizio: 2014-12-03): "In Ember 2.0, adotteremo un" DOM virtuale "e un modello di flusso di dati che abbraccia il migliori idee di React e semplifica la comunicazione tra i componenti ".
Inoltre, Angular.js 2.0 sta implementando molti dei concetti discussi qui.
Aggiornamento 4
Devo approfondire alcune questioni per rispondere a questo commento di Igwe Kalu:
"non è sensato confrontare React (JSX o l'output della compilazione) con JavaScript semplice, quando React alla fine si riduce a JavaScript semplice. [...] Qualunque strategia React utilizzi per l'inserimento DOM può essere applicata senza usare React. Detto questo, non aggiunge alcun vantaggio speciale se si considera la funzionalità in questione oltre alla praticità ". (commento completo qui )
Nel caso in cui non fosse abbastanza chiaro, in parte della mia risposta sto confrontando le prestazioni dell'operare direttamente sul DOM nativo (implementato come oggetti host nel browser) rispetto al DOM falso / virtuale di React (implementato in JavaScript). Il punto che stavo cercando di sottolineare è che il DOM virtuale implementato in JavaScript può superare il vero DOM implementato in C ++ e non che React può superare JavaScript (il che ovviamente non avrebbe molto senso dal momento che è scritto in JavaScript). Il mio punto era che il codice C ++ "nativo" non è sempre garantito per essere più veloce di JavaScript "non nativo". Usare React per illustrare quel punto era solo un esempio.
Ma questo commento ha toccato un problema interessante. In un certo senso è vero che non hai bisogno di alcun framework (React, Angular o jQuery) per qualsiasi motivo (come prestazioni, portabilità, funzionalità) perché puoi sempre ricreare ciò che fa il framework per te e reinventare la ruota - se puoi giustificare il costo, cioè.
Ma - come Dave Smith ha messo bene in Come perdere il punto quando si confrontano le prestazioni del framework Web : "Quando si confrontano due framework Web, la domanda non è: la mia app può essere veloce con il framework X. La domanda è: la mia app sarà veloce con framework X."
Nella mia risposta del 2011 a: Quali sono alcuni motivi tecnici empirici per non usare jQuery Spiego un problema simile, che non è impossibile scrivere un codice di manipolazione DOM portatile senza una libreria come jQuery, ma che le persone raramente lo fanno.
Quando si usano linguaggi di programmazione, librerie o framework, le persone tendono ad usare i modi più convenienti o idiomatici di fare le cose, non quelli perfetti ma scomodi. Il vero valore di buone strutture è rendere facile ciò che altrimenti sarebbe difficile da fare - e il segreto sta nel rendere le cose giuste convenienti. Il risultato ha ancora esattamente lo stesso potere a tua disposizione della forma più semplice di calcolo lambda o della macchina di Turing più primitiva, ma l'espressività relativa di alcuni concetti significa che quegli stessi concetti tendono ad essere espressi più facilmente o affatto, e che le giuste soluzioni non sono solo possibili ma effettivamente implementate ampiamente.
Aggiornamento 5
Reagisci + Prestazioni =? l'articolo di Paul Lewis del luglio 2015 mostra un esempio in cui React è più lento di JavaScript vaniglia scritto a mano per un elenco infinito di immagini di Flickr, che è particolarmente significativo sui dispositivi mobili. Questo esempio mostra che tutti dovrebbero sempre testare le prestazioni per casi d'uso specifici e piattaforme e dispositivi target specifici.
Grazie a Kevin Lozandier per averlo portato alla mia attenzione .