Update: Diese Antwort scheint ziemlich beliebt zu sein, daher habe ich mir etwas Zeit genommen, um sie ein wenig aufzuräumen, neue Informationen hinzuzufügen und einige Dinge zu klären, von denen ich dachte, dass sie nicht klar genug sind. Bitte kommentieren Sie, wenn Sie der Meinung sind, dass weitere Informationen oder Updates erforderlich sind.
Die meisten Ihrer Anliegen sind wirklich eine Frage der Meinung und der persönlichen Präferenz, aber ich werde versuchen, so objektiv wie möglich zu antworten:
Native vs. Compiled
Schreiben Sie JavaScript in Vanille-JavaScript, schreiben Sie CSS in CSS, schreiben Sie HTML in HTML.
Damals gab es heftige Debatten darüber, ob man native Assembly von Hand schreiben oder eine höhere Sprache wie C verwenden sollte, damit der Compiler Assembly-Code für Sie generiert. Schon vorher weigerten sich die Leute, Assemblern zu vertrauen , und zogen es vor, systemeigenen Maschinencode von Hand zu schreiben ( und ich mache keine Witze ).
Mittlerweile gibt es viele Leute, die HTML in Haml oder Jade , CSS in Sass oder Less und JavaScript in CoffeeScript oder TypeScript schreiben . Es ist da. Es klappt. Manche Leute bevorzugen es, andere nicht.
Der Punkt ist, dass es nichts grundsätzlich Falsches ist, kein JavaScript in Vanille-JavaScript, kein CSS in CSS und kein HTML in HTML zu schreiben. Es ist wirklich eine Frage der Präferenz.
Interne und externe DSLs
Bei der Stilkapselung mit Shadow DOM React muss stattdessen CSS in JavaScript geschrieben werden. Nicht hübsch.
Schön oder nicht, es ist sicherlich ausdrucksstark. JavaScript ist eine sehr leistungsfähige Sprache, die viel leistungsfähiger ist als CSS (einschließlich aller CSS-Präprozessoren). Es hängt davon ab, ob Sie für diese Art von Dingen interne oder externe DSLs bevorzugen. Auch hier eine Frage der Präferenz.
(Hinweis: Ich habe über die Inline-Stile in React gesprochen , auf die in der ursprünglichen Frage verwiesen wurde.)
Arten von DSLs - Erklärung
Update: Nachdem ich meine Antwort einige Zeit nach dem Schreiben gelesen habe, denke ich, dass ich erklären muss, was ich hier meine. DSL ist eine domänenspezifische Sprache und kann entweder intern (unter Verwendung der Syntax der Hostsprache wie JavaScript - wie zum Beispiel React without JSX oder wie die oben erwähnten Inline-Stile in React) oder extern (unter Verwendung einer anderen Syntax) sein als die Host-Sprache - wie in diesem Beispiel würde CSS (eine externe DSL) in JavaScript inlinieren).
Dies kann verwirrend sein, da in einigen Literaturstellen andere Begriffe als "intern" und "extern" verwendet werden, um solche DSL-Typen zu beschreiben. Manchmal wird anstelle von „internen“ „embedded“ verwendet , aber das Wort „embedded“ kann verschiedene Dinge bedeutet - zum Beispiel Lua wird beschrieben als „Lua: eine erweiterbare eingebettete Sprache“ , wo eingebettet hat nichts mit eingebettetem (intern) DSL zu tun (in In diesem Sinne ist es genau umgekehrt (ein externes DSL), aber es bedeutet, dass es in dem gleichen Sinne eingebettet ist, wie beispielsweise SQLite eine eingebettete Datenbank ist. Es gibt sogar eLua, wo "e" in einem dritten Sinne für "eingebettet" steht - das ist für eingebettete Systeme gedacht! Deshalb verwende ich den Begriff "eingebettetes DSL" nicht gern, weil Dinge wie eLua "DSLs" sein können, die in zweierlei Hinsicht "eingebettet" sind, aber überhaupt kein "eingebettetes DSL" sind!
Erschwerend kommt hinzu, dass einige Projekte noch mehr Verwirrung stiften. Z.B. Flatiron-Vorlagen werden als "DSL-frei" bezeichnet, obwohl dies nur ein perfektes Beispiel für ein internes DSL mit folgender Syntax ist:map.where('href').is('/').insert('newurl');
Allerdings, als ich schrieb "JavaScript ist eine sehr mächtige Sprache, viel mächtiger als CSS (sogar mit einem der CSS-Präprozessoren). Es hängt davon ab, ob Sie interne oder externe DSLs für solche Dinge bevorzugen. eine Frage der Präferenz. " Ich habe über diese beiden Szenarien gesprochen:
Ein:
/** @jsx React.DOM */
var colored = {
color: myColor
};
React.renderComponent(<div style={colored}>Hello World!</div>, mountNode);
Zwei:
// SASS:
.colored {
color: $my-color;
}
// HTML:
<div class="colored">Hello World!</div>
Das erste Beispiel verwendet das, was in der Frage beschrieben wurde als: "Schreiben von CSS in JavaScript. Nicht schön." Das zweite Beispiel verwendet Sass. Ich bin damit einverstanden, dass die Verwendung von JavaScript zum Schreiben von CSS möglicherweise nicht hübsch ist (für einige Definitionen von "hübsch"), aber es gibt einen Vorteil, dies zu tun.
Ich kann Variablen und Funktionen in Sass haben, aber sind sie lexikalisch oder dynamisch? Sind sie statisch oder dynamisch typisiert? Stark oder schwach? Was ist mit den numerischen Typen? Typumwandlung? Welche Werte sind wahr und welche falsch? Kann ich Funktionen höherer Ordnung haben? Rekursion? Schwanz ruft? Lexikalische Verschlüsse? Werden sie in normaler Reihenfolge oder in anwendbarer Reihenfolge bewertet? Gibt es eine faule oder eifrige Bewertung? Werden Argumente zu Funktionen als Wert oder als Referenz übergeben? Sind sie veränderlich? Unveränderlich? Hartnäckig? Was ist mit Objekten? Klassen? Prototypen? Erbe?
Das sind keine trivialen Fragen und dennoch muss ich Antworten auf sie wissen, wenn ich Sass- oder Less-Code verstehen will. Ich kenne diese Antworten für JavaScript bereits, was bedeutet, dass ich bereits jede interne DSL (wie die Inline-Stile in React) auf diesen Ebenen verstehe. Wenn ich also React verwende, muss ich nur eine Reihe von Antworten auf diese (und viele ähnliche) kennen ) fragen, während ich bei zb. Sass und Lenker Dann muss ich drei Sätze dieser Antworten kennen und ihre Auswirkungen verstehen.
Es ist nicht so, dass die eine oder andere Weise immer besser ist, aber jedes Mal, wenn Sie eine andere Sprache in den Mix einführen, zahlen Sie einen Preis, der auf den ersten Blick nicht so offensichtlich ist, und dieser Preis ist Komplexität.
Ich hoffe, ich habe geklärt, was ich ursprünglich ein bisschen gemeint habe.
Datenbindung
Bidirektionale Bindung
Dies ist ein wirklich interessantes Thema und in der Tat auch eine Frage der Präferenz. Beidseitig ist nicht immer besser als einseitig. Es ist eine Frage, wie Sie den veränderlichen Zustand in Ihrer Anwendung modellieren möchten. Ich habe bidirektionale Bindungen immer als eine Idee angesehen, die den Prinzipien der funktionalen Programmierung etwas zuwiderläuft, aber funktionale Programmierung ist nicht das einzige Paradigma, das funktioniert. Einige Leute bevorzugen diese Art von Verhalten, und beide Ansätze scheinen in der Praxis ziemlich gut zu funktionieren. Wenn Sie sich für die Details der Entwurfsentscheidungen im Zusammenhang mit der Modellierung des Zustands in React interessieren, sehen Sie sich den Vortrag von Pete Hunt (in der Frage verlinkt) und den Vortrag von Tom Occhino und Jordan Walke an , die ihn sehr gut erklären meine Meinung.
Update: Siehe auch einen weiteren Vortrag von Pete Hunt: Sei vorhersehbar, nicht korrekt: Funktionale DOM-Programmierung .
Update 2: Es ist erwähnenswert, dass viele Entwickler gegen den bidirektionalen Datenfluss oder die bidirektionale Bindung argumentieren , manche nennen es sogar ein Anti-Pattern. Nehmen wir zum Beispiel die Flux- Anwendungsarchitektur, die explizit das MVC- Modell (das sich für große Facebook- und Instagram-Anwendungen als schwer skalierbar erwiesen hat) zugunsten eines streng unidirektionalen Datenflusses vermeidet (siehe Hacker Way: Überdenken der Web-App-Entwicklung bei Facebook talk by) Tom Occhino, Jing Chen und Pete Hunt für eine gute Einführung). Auch viel Kritik gegen AngularJS (Das beliebteste Webframework, das lose auf dem MVC-Modell basiert und für die bidirektionale Datenbindung bekannt ist) enthält Argumente gegen diesen bidirektionalen Datenfluss, siehe:
Update 3: Ein weiterer interessanter Artikel, der einige der oben diskutierten Probleme gut erklärt, ist die Dekonstruktion von ReactJSs Flux - Keine Verwendung von MVC mit ReactJS von Mikael Brassman, Autor von RefluxJS (einer einfachen Bibliothek für unidirektionale Datenfluss-Anwendungsarchitektur, die von Flux inspiriert ist).
Update 4: Ember.js entfernt sich derzeit von der bidirektionalen Datenbindung und wird in zukünftigen Versionen standardmäßig in eine Richtung ausgeführt. Siehe: Die Zukunft von Ember Vortrag von Stefan Penner vom Embergarten Symposium in Toronto am 15. November 2014.
Update 5: Siehe auch: Der Weg zu Ember 2.0 RFC - interessante Diskussion in der Pull-Anfrage von Tom Dale :
"Als wir die ursprüngliche Vorlagenebene entwarfen, stellten wir fest, dass die bidirektionale Bindung aller Daten nicht sehr schädlich ist. Wenn Sie keine bidirektionale Bindung festlegen, handelt es sich de facto um eine einseitige Bindung!
Seitdem haben wir (mit der Hilfe unserer Freunde bei React) festgestellt, dass Komponenten Daten an ihre Kinder weitergeben möchten, ohne sich vor abwegigen Mutationen hüten zu müssen.
Darüber hinaus wird die Kommunikation zwischen Komponenten häufig auf natürliche Weise als Ereignis oder Rückruf ausgedrückt . Dies ist in Ember möglich, aber die Dominanz von bidirektionalen Datenbindungen führt Menschen häufig dazu, bidirektionale Bindungen als Kommunikationskanal zu verwenden . Erfahrene Ember-Entwickler machen diesen Fehler (normalerweise) nicht, aber er ist einfach zu begehen. " [Hervorhebung hinzugefügt]
Native vs. VM
Native Browser-Unterstützung (lesen Sie "garantiert schneller")
Nun endlich etwas, das keine Ansichtssache ist.
Eigentlich ist es hier genau umgekehrt. Natürlich kann "nativer" Code in C ++ geschrieben werden, aber in was sind die JavaScript-Engines Ihrer Meinung nach geschrieben?
Tatsächlich sind die JavaScript-Engines in den Optimierungen, die sie heute verwenden, wirklich erstaunlich - und nicht nur V8, sondern auch SpiderMonkey und sogar Chakra glänzen heutzutage. Beachten Sie, dass der Code bei JIT-Compilern nicht nur so nativ ist, wie er möglicherweise sein kann, sondern auch Möglichkeiten zur Laufzeitoptimierung bietet, die in statisch kompiliertem Code einfach nicht möglich sind.
Wenn Leute denken, dass JavaScript langsam ist, meinen sie normalerweise JavaScript, das auf das DOM zugreift. Das DOM ist langsam. Es ist nativ, in C ++ geschrieben und ist aufgrund der Komplexität, die es implementieren muss, höllisch langsam.
Öffne deine Konsole und schreibe:
console.dir(document.createElement('div'));
und sehen, wie viele Eigenschaften ein leeres div
Element, das nicht einmal an das DOM angehängt ist, implementieren muss. Dies sind nur die Eigenschaften der ersten Ebene , die "eigene Eigenschaften" sind, dh. nicht von der Prototypenkette geerbt:
ausrichten, abwarten, aufVolumenändern, aufUpdate, aufhängen, einreichen, aufinstalliert, aufzeigen, aufwählen, aufsuchen, aufgesucht, aufrollen, aufgrösse, aufsetzen, auftauschen, auffortschritt, aufspielen, aufspielen, aufhören, aufmausen, aufmausen, aufmausen onmouseenter, onmousedown, onloadstart, onloadedmetadata, onloadeddata, onload, onkeyup, onkeypress, onkeydown, oninvalid, oninput, onfocus, onerror, onended, onemptied, ondurationchange, ondrop, ondragstart, ondragover, ondragleave, ondragdragd oncontextmenu, onclose, onclick, onchange, oncanplaythrough, oncanplay, oncancel, onblur, onabort, rechtschreibprüfung, isContentEditable, contentEditable, outerText, innerText, accessKey, hidden, webkitdropzone, draggable, tabIndex, dir, translate, langCild, lastE child, tabIndexfirstElementChild, children, nextElementSibling, previousElementSibling, onwheel, onwebkitfullscreenerror, onwebkitfullscreenchange, onselectstart, onsearch, onpaste, oncut, oncopy, onbeforepaste, onbeforecut, onbeforecopy, onbeforecopy clientHeight, clientWidth, clientTop, clientLeft, offsetParent, offsetHeight, offsetWidth, offsetTop, offsetLeft, localName, Präfix, NamespaceURI, ID, Stil, Attribute, tagName, parentElement, textContent, baseURI, ownerDocument, nextSibling, previousSibling, firstCildhild parentNode, nodeType, nodeValue, nodeNameoncopy, onbeforepaste, onbeforecut, onbeforecopy, webkitShadowRoot, Dataset, Klassenliste, Klassenname, OuterHTML, InnerHTML, ScrollHeight, ScrollWidth, ScrollTop, ScrollLeft, ClientHeight, ClientWidth, ClientTop, ClientLeftH OffsetWidthParent NamespaceURI, ID, Stil, Attribute, TagName, ParentElement, TextContent, BaseURI, OwnerDocument, NextSibling, PreviousSibling, LastChild, FirstChild, ChildNodes, ParentNode, NodeType, NodeValue, NodeNameoncopy, onbeforepaste, onbeforecut, onbeforecopy, webkitShadowRoot, Dataset, Klassenliste, Klassenname, OuterHTML, InnerHTML, ScrollHeight, ScrollWidth, ScrollTop, ScrollLeft, ClientHeight, ClientWidth, ClientTop, ClientLeftH OffsetWidthParent NamespaceURI, ID, Stil, Attribute, 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
Viele von ihnen sind tatsächlich verschachtelte Objekte - um die (eigenen) Eigenschaften eines leeren Native div
in Ihrem Browser zu sehen, sehen Sie sich diese Geige an .
Ich meine ernsthaft, onvolumechange Eigenschaft auf jedem einzelnen div Knoten? Ist das ein Fehler? Nein, es ist nur eine traditionelle DOM Level 0-Ereignismodellversion eines der Ereignishandler, " die von allen HTML-Elementen unterstützt werden muss , sowohl als Inhaltsattribute als auch als IDL-Attribute" [Hervorhebung hinzugefügt] in Abschnitt 6.1.6.2 der HTML-Spezifikation von W3C - kein Weg daran vorbei.
In der Zwischenzeit sind dies die Eigenschaften der ersten Ebene eines Fake-DOM div
in React:
Requisiten, _Besitzer, _LifeCycleState, _pendingProps, _pendingCallbacks, _pendingOwner
Ein ziemlicher Unterschied, nicht wahr? In der Tat ist dies das gesamte Objekt zu JSON (serialisiert LIVE - DEMO ), weil hey Sie tatsächlich kann es zu JSON serialisiert werden, da es keine zirkulären Referenzen enthält - etwas undenkbar in der Welt der nativen DOM ( wo es nur eine Ausnahme auslösen würde ):
{
"props": {},
"_owner": null,
"_lifeCycleState": "UNMOUNTED",
"_pendingProps": null,
"_pendingCallbacks": null,
"_pendingOwner": null
}
Dies ist so ziemlich der Hauptgrund, warum React schneller sein kann als das native Browser-DOM - weil es dieses Durcheinander nicht implementieren muss .
In dieser Präsentation von Steven Luscher erfahren Sie , was schneller ist: Natives DOM in C ++ oder gefälschtes DOM, das vollständig in JavaScript geschrieben ist. Es ist eine sehr faire und unterhaltsame Präsentation.
Update: Ember.js wird in zukünftigen Versionen ein virtuelles DOM verwenden, das stark von React inspiriert ist, um die Leistung zu verbessern. Siehe: Die Zukunft von Ember Vortrag von Stefan Penner vom Embergarten Symposium in Toronto am 15. November 2014.
Zusammenfassend lässt sich sagen, dass Features von Webkomponenten wie Vorlagen, Datenbindungen oder benutzerdefinierte Elemente viele Vorteile gegenüber React bieten. Solange das Dokumentobjektmodell selbst nicht erheblich vereinfacht wird, ist die Leistung keine davon.
Aktualisieren
Zwei Monate nachdem ich diese Antworten gepostet hatte, gab es einige Neuigkeiten, die hier relevant sind. Wie ich gerade auf Twitter geschrieben habe , nutzt die neueste Version des Atom- Texteditors, der von GitHub in JavaScript geschrieben wurde, Facebooks React, um eine bessere Leistung zu erzielen, obwohl laut Wikipedia "Atom auf Chrom basiert und in C ++ geschrieben" die volle Kontrolle über hat die native C ++ - DOM-Implementierung (siehe The Nucleus of Atom ) und Web Components werden garantiert unterstützt, da es mit einem eigenen Webbrowser ausgeliefert wird. Es ist nur ein aktuelles Beispiel für ein reales Projekt, das jede andere Art von Optimierung hätte verwenden können, die für Webanwendungen normalerweise nicht verfügbar ist, und es hat sich dennoch für die Verwendung von React entschieden, das selbst in JavaScript geschrieben ist, um die beste Leistung zu erzielen, obwohl Atom wurde ursprünglich nicht mit React erstellt, daher war es keine triviale Änderung.
Update 2
Es ist ein interessanter Vergleich von Todd Parker mit Webpage Leistung vergleichen TodoMVC in Angular, Backbone geschrieben Beispiele, Ember, Polymer, CanJS, YUI, Knockout, Reaktion und Shoestring. Dies ist der objektivste Vergleich, den ich bisher gesehen habe. Was hier von Bedeutung ist, ist, dass alle entsprechenden Beispiele von Experten in all diesen Frameworks geschrieben wurden, alle auf GitHub verfügbar sind und von jedem verbessert werden können, der denkt, dass ein Teil des Codes für eine schnellere Ausführung optimiert werden könnte.
Update 3
Ember.js wird in zukünftigen Versionen eine Reihe von Funktionen von React enthalten, die hier behandelt werden (einschließlich eines virtuellen DOM und einer unidirektionalen Datenbindung, um nur einige zu nennen). Dies bedeutet, dass die aus React stammenden Ideen bereits in andere Frameworks migriert werden. Siehe: Der Weg zu Ember 2.0 RFC - interessante Diskussion in der Pull-Anfrage von Tom Dale (Startdatum: 03.12.2014): "In Ember 2.0 werden wir ein" virtuelles DOM "- und Datenflussmodell übernehmen, das die Die besten Ideen von Reagieren und vereinfachen die Kommunikation zwischen Komponenten. "
Auch Angular.js 2.0 implementiert viele der hier diskutierten Konzepte.
Update 4
Ich muss auf einige Punkte eingehen, um diesen Kommentar von Igwe Kalu zu beantworten:
"Es ist nicht sinnvoll, React (JSX oder die Kompilierungsausgabe) mit reinem JavaScript zu vergleichen, wenn React letztendlich auf reinem JavaScript reduziert wird. [...] Jede Strategie, die React für das Einfügen von DOMs verwendet, kann ohne React angewendet werden bringt keine besonderen Vorteile mit sich, wenn man die fragliche Funktion außer der Bequemlichkeit betrachtet. " (vollständiger Kommentar hier )
Für den Fall, dass es nicht klar genug war, vergleiche ich in einem Teil meiner Antwort die Leistung des direkten Betriebs auf dem nativen DOM (implementiert als Host-Objekte im Browser) mit dem gefälschten / virtuellen DOM von React (implementiert in JavaScript). Der Punkt, den ich anstrebte, ist, dass das in JavaScript implementierte virtuelle DOM das in C ++ implementierte reale DOM übertreffen kann und nicht, dass React JavaScript übertreffen kann (was offensichtlich keinen Sinn ergibt , da es in JavaScript geschrieben ist). Mein Punkt war, dass "nativer" C ++ - Code nicht immer schneller ist als "nicht nativer" JavaScript. Die Verwendung von React zur Veranschaulichung dieses Punktes war nur ein Beispiel.
Dieser Kommentar berührte jedoch ein interessantes Thema. In gewissem Sinne ist es wahr, dass Sie aus keinem Grund (wie Leistung, Portabilität, Funktionen) ein Framework (React, Angular oder jQuery) benötigen, da Sie jederzeit neu erstellen können, was das Framework für Sie tut, und das Rad neu erfinden können - wenn Sie können also die Kosten rechtfertigen.
Aber - wie Dave Smith legt es schön in Wie der Punkt verpassen , wenn Web - Framework zum Vergleich der Leistung : „Wenn zwei Frameworks Web zu vergleichen, ist die Frage nicht , kann mein app mit Rahmen X. schnell seine Die Frage ist wird mein app schnell mit Rahmen X."
In meiner Antwort von 2011 auf: Was sind einige empirische technische Gründe, um jQuery nicht zu verwenden? Ich erkläre ein ähnliches Problem, dass es nicht unmöglich ist, portablen DOM-Manipulationscode ohne eine Bibliothek wie jQuery zu schreiben, dies jedoch nur selten der Fall ist.
Bei der Verwendung von Programmiersprachen, Bibliotheken oder Frameworks tendieren die Leute dazu, die bequemsten oder idiomatischsten Methoden zu verwenden, nicht die perfekten, sondern die unbequemsten. Der wahre Wert guter Frameworks besteht darin, einfach zu machen, was sonst schwer zu tun wäre - und das Geheimnis besteht darin, die richtigen Dinge bequem zu machen. Das Ergebnis ist, dass Ihnen immer noch genau die gleiche Kraft zur Verfügung steht wie bei der einfachsten Form der Lambda-Rechnung oder der primitivsten Turing-Maschine. Die relative Aussagekraft bestimmter Konzepte führt jedoch dazu, dass genau diese Konzepte leichter oder gar nicht zum Ausdruck gebracht werden Die richtigen Lösungen sind nicht nur möglich, sondern werden auch in großem Umfang umgesetzt.
Update 5
Reagieren + Leistung =? Der Artikel von Paul Lewis aus dem Juli 2015 zeigt ein Beispiel, in dem React langsamer ist als Vanille. JavaScript wurde von Hand für eine unendliche Liste von Flickr-Bildern geschrieben, was besonders für Mobilgeräte von Bedeutung ist. Dieses Beispiel zeigt, dass jeder die Leistung immer für bestimmte Anwendungsfälle und bestimmte Zielplattformen und -geräte testen sollte.
Dank Kevin Lozandier für ihn , um meine Aufmerksamkeit zu bringen .