Aktualizacja: ta odpowiedź wydaje się dość popularna, więc poświęciłem trochę czasu na jej oczyszczenie, dodanie nowych informacji i wyjaśnienie kilku rzeczy, które moim zdaniem nie były wystarczająco jasne. Proszę o komentarz, jeśli uważasz, że coś jeszcze wymaga wyjaśnienia lub aktualizacji.
Większość twoich obaw jest tak naprawdę kwestią opinii i osobistych preferencji, ale postaram się odpowiedzieć tak obiektywnie, jak to możliwe:
Natywny a skompilowany
Napisz JavaScript w waniliowym JavaScript, napisz CSS w CSS, napisz HTML w HTML.
W tamtych czasach trwały gorące debaty, czy należy ręcznie napisać natywny asembler, czy użyć języka wyższego poziomu, takiego jak C, aby kompilator wygenerował dla ciebie kod asemblera. Jeszcze wcześniej ludzie nie ufali asemblerom i woleli pisać natywny kod maszynowy ręcznie ( i nie żartuję ).
Tymczasem dziś wiele osób pisze HTML w Haml lub Jade , CSS w Sass lub Less i JavaScript w CoffeeScript lub TypeScript . Jest tutaj. To działa. Niektórzy wolą, inni nie.
Chodzi o to, że nie ma nic zasadniczo złego w nie pisaniu JavaScript w waniliowym JavaScript, CSS w CSS i HTML w HTML. To naprawdę kwestia preferencji.
Wewnętrzne a zewnętrzne DSL
Zamiast tego istnieje styl enkapsulacji za pomocą Shadow DOM React, który wymaga pisania CSS w JavaScript. Nie ładna
Ładne czy nie, z pewnością jest wyraziste. JavaScript jest bardzo potężnym językiem, znacznie potężniejszym niż CSS (nawet włączając w to którykolwiek z preprocesorów CSS). To zależy od tego, czy wolisz wewnętrzne czy zewnętrzne DSL dla tego rodzaju rzeczy. Znowu kwestia preferencji.
(Uwaga: mówiłem o stylach wbudowanych w programie React, do których odwołano się w pierwotnym pytaniu).
Rodzaje DSL - wyjaśnienie
Aktualizacja: Czytając moją odpowiedź jakiś czas po jej napisaniu, myślę, że muszę wyjaśnić, co mam na myśli. DSL jest językiem specyficznym dla domeny i może być wewnętrzny (przy użyciu składni języka hosta, takiego jak JavaScript - jak na przykład React bez JSX, lub jak style wbudowane w React wspomniane powyżej) lub może być zewnętrzny (przy użyciu innej składni niż język hosta - tak jak w tym przykładzie byłoby wstawianie CSS (zewnętrznego DSL) wewnątrz JavaScript).
Może to być mylące, ponieważ niektóre literatury używają terminów innych niż „wewnętrzny” i „zewnętrzny” do opisania tego rodzaju DSL. Czasami zamiast „wewnętrznego” używa się słowa „osadzony”, ale słowo „osadzony” może oznaczać różne rzeczy - na przykład Lua jest opisana jako „Lua: rozszerzalny język osadzony”, w którym osadzony nie ma nic wspólnego z wbudowanym (wewnętrznym) DSL (w w którym sensie jest wręcz przeciwnie - zewnętrzna DSL), ale oznacza to, że jest osadzony w tym samym sensie, co, powiedzmy, SQLite jest osadzoną bazą danych. Istnieje nawet eLua, w którym „e” oznacza „osadzony” w trzecim znaczeniu - że jest przeznaczony dla systemów wbudowanych! Dlatego nie lubię używać terminu „wbudowany DSL”, ponieważ takie rzeczy jak eLua mogą być „DSL”, które są „osadzone” na dwa różne sposoby, ale wcale nie są „osadzonymi DSL”!
Co gorsza, niektóre projekty wprowadzają jeszcze więcej zamieszania w miksie. Na przykład. Szablony Flatiron są opisane jako „bez DSL”, podczas gdy w rzeczywistości jest to idealny przykład wewnętrznej DSL o składni takiej jak:map.where('href').is('/').insert('newurl');
To powiedziawszy, kiedy napisałem: „JavaScript jest bardzo potężnym językiem, znacznie potężniejszym niż CSS (nawet włączając którykolwiek z preprocesorów CSS). Zależy to od tego, czy wolisz wewnętrzne czy zewnętrzne DSL dla tego rodzaju rzeczy. Ponownie, kwestia preferencji ”. Mówiłem o tych dwóch scenariuszach:
Jeden:
/** @jsx React.DOM */
var colored = {
color: myColor
};
React.renderComponent(<div style={colored}>Hello World!</div>, mountNode);
Dwa:
// SASS:
.colored {
color: $my-color;
}
// HTML:
<div class="colored">Hello World!</div>
W pierwszym przykładzie użyto tego, co opisano w pytaniu jako: „pisanie CSS w JavaScript. Niezbyt ładne”. Drugi przykład wykorzystuje Sass. Chociaż zgadzam się, że używanie JavaScript do pisania CSS może nie być ładne (w przypadku niektórych definicji „ładnych”), ale ma jedną zaletę.
Mogę mieć zmienne i funkcje w Sass, ale czy mają one zakres leksykalny czy dynamiczny? Czy są wpisywane statycznie czy dynamicznie? Mocno czy słabo? Co z typami liczbowymi? Typ kooperacji? Które wartości są prawdziwe, a które fałszywe? Czy mogę mieć funkcje wyższego rzędu? Rekurencja? Woła ogon? Zamknięcia leksykalne? Czy są oceniane w normalnej kolejności lub kolejności stosowania? Czy jest leniwa lub chętna ocena? Czy argumenty funkcji są przekazywane przez wartość czy przez odwołanie? Czy są zmienne? Niezmienny? Uporczywy? Co z przedmiotami? Klasy? Prototypy? Dziedzictwo?
To nie są trywialne pytania, a jednak muszę znać odpowiedzi, jeśli chcę zrozumieć kod Sass or Less. Znam już odpowiedzi na JavaScript, co oznacza, że już rozumiem każdą wewnętrzną DSL (jak style wbudowane w React) na tych samych poziomach, więc jeśli używam React, muszę znać tylko jeden zestaw odpowiedzi na te (i wiele podobnych ) pytania, podczas gdy używam np. Sass i Kierownica muszę więc znać trzy zestawy tych odpowiedzi i zrozumieć ich konsekwencje.
Nie oznacza to, że w ten czy inny sposób jest zawsze lepszy, ale za każdym razem, gdy wprowadzasz inny język do miksu, płacisz pewną cenę, która może nie być tak oczywista na pierwszy rzut oka, a ta cena jest złożona.
Mam nadzieję, że wyjaśniłem trochę, co pierwotnie miałem na myśli.
Powiązanie danych
Wiązanie dwustronne
To naprawdę interesujący temat, a właściwie kwestia preferencji. Dwukierunkowa nie zawsze jest lepsza niż jednokierunkowa. To pytanie, jak chcesz modelować stan zmiennych w swojej aplikacji. Zawsze postrzegałem dwustronne powiązania jako pomysł nieco sprzeczny z zasadami programowania funkcjonalnego, ale programowanie funkcjonalne nie jest jedynym paradygmatem, który działa, niektórzy wolą takie zachowanie i oba podejścia wydają się działać całkiem dobrze w praktyce. Jeśli interesują Cię szczegóły decyzji projektowych związanych z modelowaniem stanu w React, obejrzyj przemówienie Pete Hunta (powiązane z pytaniem) oraz przemówienie Toma Occhino i Jordana Walke'a, którzy bardzo dobrze to wyjaśniają w moja opinia.
Aktualizacja: Zobacz także inne przemówienie Pete Hunta: Bądź przewidywalny, niepoprawny: funkcjonalne programowanie DOM .
Aktualizacja 2: Warto zauważyć, że wielu programistów sprzeciwia się dwukierunkowemu przepływowi danych lub dwukierunkowemu wiązaniu, niektórzy nawet nazywają to anty-wzorcem. Weźmy na przykład Flux architekturę aplikacji, który wyraźnie unika MVC model (który okazał się być trudne do skalowania dla dużych Facebook i Instagram aplikacji) na rzecz ściśle jednokierunkowy przepływ danych (patrz Way Hacker: Rethinking Web Development App Facebook rozmawiać przez Tom Occhino, Jing Chen i Pete Hunt za dobre wprowadzenie). Również wiele krytyki przeciwko AngularJS (najpopularniejszy framework sieciowy, który jest luźno oparty na modelu MVC, znany z dwukierunkowego wiązania danych) zawiera argumenty przeciwko temu dwukierunkowemu przepływowi danych, patrz:
Aktualizacja 3: Kolejnym interesującym artykułem, który ładnie wyjaśnia niektóre z omawianych powyżej problemów, jest Dekonstruowanie strumienia ReactJS - nieużywanie MVC z ReactJS przez Mikaela Brassmana, autora RefluxJS (prosta biblioteka dla architektury aplikacji do jednokierunkowego przepływu danych zainspirowana przez Flux).
Aktualizacja 4: Ember.js obecnie odchodzi od dwukierunkowego wiązania danych, aw przyszłych wersjach domyślnie będzie jednokierunkowy. Zobacz: Przyszłość Embera przemówienie Stefana Pennera z Embergarten Symposium w Toronto 15 listopada 2014 r.
Aktualizacja 5: Zobacz także: Droga do Ember 2.0 RFC - ciekawa dyskusja w żądaniu ściągnięcia Toma Dale'a :
„Kiedy zaprojektowaliśmy oryginalną warstwę szablonów, doszliśmy do wniosku, że wykonanie dwukierunkowego powiązania wszystkich danych nie było zbyt szkodliwe: jeśli nie ustawisz powiązania dwukierunkowego, jest to de facto wiązanie jednokierunkowe!
Od tego czasu zdaliśmy sobie sprawę (z pewną pomocą naszych przyjaciół z React), że komponenty chcą móc przekazywać dane swoim dzieciom bez konieczności pilnowania nieprzyzwoitych mutacji.
Ponadto komunikacja między komponentami jest najczęściej najbardziej naturalnie wyrażana jako zdarzenia lub wywołania zwrotne . Jest to możliwe w Ember, ale dominacja dwukierunkowych powiązań danych często prowadzi ludzi na ścieżkę korzystania z dwustronnych powiązań jako kanału komunikacji . Doświadczeni programiści Ember (zwykle) nie popełniają tego błędu, ale łatwo go popełnić. ” [Podkreślenie dodane]
Natywny vs. VM
Natywna obsługa przeglądarki (czytaj „z pewnością szybsze”)
Teraz wreszcie coś, co nie jest kwestią opinii.
W rzeczywistości jest dokładnie na odwrót. Oczywiście „rodzimy” kod może być napisany w C ++, ale jak myślisz, w czym są napisane silniki JavaScript?
W rzeczywistości silniki JavaScript są naprawdę niesamowite w optymalizacjach, których używają dzisiaj - i nie tylko V8, ale także SpiderMonkey, a nawet Chakra. I pamiętaj, że w kompilatorach JIT kod jest nie tylko tak natywny, jak to tylko możliwe, ale istnieją również możliwości optymalizacji czasu wykonywania, których nie da się zrobić w żadnym statycznie skompilowanym kodzie.
Kiedy ludzie myślą, że JavaScript działa wolno, zwykle mają na myśli JavaScript, który uzyskuje dostęp do DOM. DOM jest wolny. Jest natywny, napisany w C ++, a jednak jest powolny jak diabli ze względu na złożoność, którą musi wdrożyć.
Otwórz konsolę i napisz:
console.dir(document.createElement('div'));
i zobacz, ile właściwości div
musi zaimplementować pusty element, który nawet nie jest dołączony do DOM. Są to tylko właściwości pierwszego poziomu , które są „właściwościami własnymi”, tj. nie odziedziczony z łańcucha prototypów:
wyrównaj, oczekuj, zmień, ontimeupdate, onsuspend, onsubmit, onstalled, onshow, onselect, onseeking, onseeked, onscroll, onresize, onreset, onratechange, onprogress, onplaying, onplay, onpause, onmouse onmouse onmouse onmouse onmouse onmouseenter, onmousedown, onloadstart, onloadedmetadata, onloadeddata, onload, onkeyup, onkeypress, onkeydown, oninvalid, oninput, onfocus, onerror, onended, onemptied, ondurationchange, ondrop, ondragstart, ondragag, ondrdrd oncontextmenu, onclose, onclick, onchange, oncanplaythrough, oncanplay, oncancel, onblur, onabort, spellcheck, isContentEditable, contentEditable, outerText, innerText, accessKey, ukryty, webkitdropzone, przeciągalny, tabIndex, reż, tytuł, tłumaczeniefirstElementChild, dzieci, nextElementSibling, previousElementSibling, onwheel, onwebkitfullscreenerror, onwebkitfullscreenchange, onselectstart, onsearch, onpaste, oncut, oncopy, onbeforepaste, onbeforecut, onbeforecopy, webWitTextHewLMLText, klasa clientHeight, clientWidth, clientTop, clientLeft, offsetParent, offsetHeight, offsetWidth, offsetTop, offsetLeft, localName, prefiks, przestrzeń nazwURI, id, styl, atrybuty, tagName, parentElement, textContent, baseURI, ownerDocument, nextSibling, previousSibling, lastHild child, firstCild parentNode, nodeType, nodeValue, nodeNameonline przestrzeń nazwURI, id, styl, atrybuty, tagName, parentElement, textContent, baseURI, ownerDocument, nextSibling, previousSibling, lastChild, firstChild, childNodes, parentNode, nodeType, nodeValue, nodeNameonline przestrzeń nazwURI, id, styl, atrybuty, 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
Wiele z nich to tak naprawdę obiekty zagnieżdżone - aby zobaczyć właściwości drugiego poziomu (własne) pustego obiektu macierzystego div
w przeglądarce, zobacz to skrzypce .
Mówię poważnie, właściwość onvolumechange na każdym węźle div? Czy to pomyłka? Nie, to tylko starsza wersja tradycyjnego modelu zdarzeń DOM Level 0 jednej z procedur obsługi zdarzeń „, która musi być obsługiwana przez wszystkie elementy HTML , ponieważ zarówno atrybuty treści, jak i atrybuty IDL” [podkreślenie dodane] w sekcji 6.1.6.2 specyfikacji HTML autor: W3C - nie da się tego obejść.
Tymczasem są to właściwości pierwszego poziomu fałszywej DOM div
w React:
rekwizyty, _owner, _lifeCycleState, _pendingProps, _pendingCallbacks, _pendingOwner
Całkiem różnica, prawda? W rzeczywistości jest to cały obiekt zserializowany do JSON ( LIVE DEMO ), ponieważ hej, tak naprawdę możesz serializować go do JSON, ponieważ nie zawiera żadnych okrągłych odniesień - coś nie do pomyślenia w świecie natywnego DOM ( gdzie po prostu rzuciłby wyjątek ):
{
"props": {},
"_owner": null,
"_lifeCycleState": "UNMOUNTED",
"_pendingProps": null,
"_pendingCallbacks": null,
"_pendingOwner": null
}
Jest to właściwie główny powód, dla którego React może być szybszy niż natywny DOM przeglądarki - ponieważ nie musi implementować tego bałaganu .
Zobacz prezentację Stevena Luschera, aby zobaczyć, co jest szybsze: natywny DOM napisany w C ++ lub fałszywy DOM napisany całkowicie w JavaScript. To bardzo uczciwa i zabawna prezentacja.
Aktualizacja: Ember.js w przyszłych wersjach będzie korzystał z wirtualnego DOM silnie zainspirowanego Reactem w celu poprawy wydajności. Zobacz: Przyszłość Embera przemówienie Stefana Pennera z Embergarten Symposium w Toronto 15 listopada 2014 r.
Podsumowując: funkcje komponentów sieci Web, takie jak szablony, powiązanie danych lub elementy niestandardowe, będą miały wiele zalet w stosunku do React, ale dopóki sam model obiektu dokumentu nie zostanie znacznie uproszczony, wydajność nie będzie jednym z nich.
Aktualizacja
Dwa miesiące po opublikowaniu tych odpowiedzi pojawiło się kilka istotnych informacji. Jak właśnie napisałem na Twitterze , najnowsza wersja edytora tekstowego Atom napisana przez GitHub w JavaScript korzysta z React Facebooka, aby uzyskać lepszą wydajność, mimo że według Wikipedii „Atom jest oparty na Chromium i napisany w C ++”, więc ma pełną kontrolę nad natywna implementacja DOM C ++ (patrz The Nucleus of Atom ) i gwarantuje obsługę komponentów Web, ponieważ jest dostarczana z własną przeglądarką internetową. To tylko najnowszy przykład projektu z prawdziwego świata, w którym można było zastosować jakąkolwiek inną optymalizację, zwykle niedostępną dla aplikacji internetowych, a jednak zdecydowano się użyć React, który sam jest napisany w JavaScript, aby osiągnąć najlepszą wydajność, mimo że Atom nie został zbudowany z Reactem, więc zrobienie tego nie było trywialną zmianą.
Aktualizacja 2
Istnieje interesujące porównanie autorstwa Todda Parkera za pomocą WebPagetest do porównania wydajności przykładów TodoMVC napisanych w Angular, Backbone, Ember, Polymer, CanJS, YUI, Knockout, React i Shoestring. To najbardziej obiektywne porównanie, jakie do tej pory widziałem. Istotne jest tutaj to, że wszystkie odpowiednie przykłady zostały napisane przez ekspertów we wszystkich tych frameworkach, wszystkie są dostępne w GitHub i mogą być ulepszane przez każdego, kto uważa, że część kodu można zoptymalizować, aby działał szybciej.
Aktualizacja 3
Ember.js w przyszłych wersjach będzie zawierał szereg funkcji React, które są tutaj omówione (w tym wirtualny DOM i jednokierunkowe wiązanie danych, by wymienić tylko kilka), co oznacza, że pomysły powstałe w React już migrują do innych frameworków. Zobacz: Droga do Ember 2.0 RFC - ciekawa dyskusja w żądaniu ściągnięcia Toma Dale'a (Data rozpoczęcia: 2014-12-03): „W Ember 2.0 wprowadzimy„ wirtualny DOM ”i model przepływu danych obejmujący najlepsze pomysły React i upraszcza komunikację między komponentami. ”
Również Angular.js 2.0 implementuje wiele omawianych tutaj pojęć.
Aktualizacja 4
Muszę rozwinąć kilka zagadnień, aby odpowiedzieć na ten komentarz Igwe Kalu:
„nie ma sensu porównywać React (JSX lub danych wyjściowych kompilacji) ze zwykłym JavaScriptem, gdy React ostatecznie redukuje się do zwykłego JavaScript. [...] Niezależnie od strategii, którą React używa do wstawiania DOM, można zastosować bez użycia React. nie dodaje żadnych specjalnych korzyści, gdy rozważa się omawianą funkcję inną niż wygoda ”. (pełny komentarz tutaj )
W przypadku, gdy nie było to wystarczająco jasne, w części mojej odpowiedzi porównuję wydajność działania bezpośrednio na natywnym DOM (zaimplementowanym jako obiekty hosta w przeglądarce) w porównaniu z fałszywą / wirtualną DOM React (zaimplementowaną w JavaScript). Chodzi mi o to, że wirtualny DOM zaimplementowany w JavaScript może przewyższać prawdziwy DOM zaimplementowany w C ++, a nie to, że React może przewyższyć JavaScript (co oczywiście nie ma większego sensu, ponieważ jest napisany w JavaScript). Chodzi mi o to, że „natywny” kod C ++ nie zawsze jest szybszy niż „nie-natywny” JavaScript. Wykorzystanie React do zilustrowania tego punktu było tylko przykładem.
Ale ten komentarz dotyczył interesującej kwestii. W pewnym sensie prawdą jest, że nie potrzebujesz żadnego frameworka (React, Angular lub jQuery) z jakiegokolwiek powodu (takiego jak wydajność, przenośność, funkcje), ponieważ zawsze możesz odtworzyć to, co robi dla ciebie frameworka i wymyślić koło - jeśli to znaczy uzasadnić koszt.
Ale - jak to ładnie ujął Dave Smith w: Jak pominąć punkt przy porównywaniu wydajności frameworka : „Porównując dwa frameworki, pytanie nie brzmi, czy moja aplikacja może być szybka z frameworkiem X. Pytanie brzmi: czy moja aplikacja będzie szybka z frameworkiem X. ”
W mojej odpowiedzi na 2011 r .: Jakie są niektóre empiryczne techniczne powody, aby nie używać jQuery , wyjaśniam podobny problem, że nie jest niemożliwe napisanie przenośnego kodu manipulacji DOM bez biblioteki takiej jak jQuery, ale ludzie rzadko to robią.
Podczas korzystania z języków programowania, bibliotek lub frameworków ludzie używają najwygodniejszych lub idiomatycznych sposobów robienia rzeczy, a nie idealnych, ale niewygodnych. Prawdziwą wartością dobrych frameworków jest ułatwienie tego, co w innym przypadku byłoby trudne do zrobienia - a sekret polega na tym, aby odpowiednie rzeczy były wygodne. W rezultacie nadal masz dokładnie taką samą moc, jak najprostsza forma rachunku lambda lub najbardziej prymitywna maszyna Turinga, ale względna ekspresja niektórych pojęć oznacza, że te same pojęcia mają tendencję do wyrażania się łatwiej lub wcale, i że właściwe rozwiązania są nie tylko możliwe, ale faktycznie wdrażane na szeroką skalę.
Aktualizacja 5
React + Performance =? artykuł Paula Lewisa z lipca 2015 r. pokazuje przykład, w którym React działa wolniej niż waniliowy JavaScript napisany ręcznie dla nieskończonej listy zdjęć Flickr, co ma szczególne znaczenie na urządzeniach mobilnych. Ten przykład pokazuje, że każdy powinien zawsze testować wydajność dla konkretnego przypadku użycia oraz określonych platform docelowych i urządzeń.
Dziękuję Kevinowi Lozandierowi za zwrócenie mojej uwagi .