Оновлення: ця відповідь здається досить популярною, тому мені знадобилося трохи часу, щоб її трохи очистити, додати нову інформацію та уточнити кілька речей, які, на мою думку, були недостатньо зрозумілими. Будь ласка, прокоментуйте, якщо ви вважаєте, що щось інше потребує уточнення чи оновлення.
Більшість ваших занепокоєнь справді є питанням думки та особистих уподобань, але я постараюся відповісти максимально об'єктивно:
Рідний проти укладений
Пишіть JavaScript у ванільному JavaScript, пишете CSS у CSS, пишете HTML у HTML.
Ще в той день були гарячі дебати про те, чи слід писати рідну асамблею вручну чи використовувати мову більш високого рівня, наприклад C, щоб компілятор генерував для вас код збірки. Ще до цього люди відмовлялися довіряти асемблерам і вважали за краще писати рідний машинний код вручну ( і я не жартую ).
Тим часом, сьогодні дуже багато людей, які пишуть HTML в Haml або Jade , CSS в Sass або Less та JavaScript в CoffeeScript або TypeScript . Це там. Це працює. Деякі люди віддають перевагу, інші - ні.
Справа в тому, що немає нічого принципово неправильного в тому, щоб не писати JavaScript у ванільному JavaScript, CSS у CSS та HTML у HTML. Це справді питання переваги.
Внутрішні та зовнішні DSL
У цьому стилі інкапсуляція, що використовує Shadow DOM React, вимагає написання CSS в JavaScript. Не дуже.
Гарне чи ні, це, безумовно, виразно. JavaScript - це дуже потужна мова, набагато потужніша за CSS (навіть включаючи будь-який з препроцесорів CSS). Це залежить від того, чи надаєте перевагу внутрішнім чи зовнішнім DSL для таких речей. Знову ж таки, питання переваги.
(Примітка. Я говорив про вбудовані стилі в React, на які згадувалося в оригінальному запитанні.)
Типи DSL - пояснення
Оновлення: Читаючи мою відповідь через деякий час після її написання, я думаю, що мені потрібно пояснити, що я тут маю на увазі. DSL - мова, що залежить від домену, і може бути внутрішньою (використовуючи синтаксис мови хосту, як-от JavaScript - наприклад, React без JSX, або як стилі вбудованого в React, згадані вище), або він може бути зовнішнім (використовуючи інший синтаксис ніж мова хосту - як у цьому прикладі буде вбудований CSS (зовнішній DSL) всередині JavaScript).
Це може бути заплутано, оскільки для опису цих видів DSL деякі літератури використовують інші терміни, ніж "внутрішня" та "зовнішня". Іноді "вбудований" використовується замість "внутрішній", але слово "вбудований" може означати різні речі - наприклад, Lua описується як "Lua: вбудована мова, що розширюється", де вбудований не має нічого спільного з вбудованим (внутрішнім) DSL (у який сенс зовсім протилежний - зовнішній DSL), але це означає, що він вбудований у тому ж сенсі, що, скажімо, SQLite - це вбудована база даних. Існує навіть eLua, де "e" означає "вбудований" в третьому сенсі - що він призначений для вбудованих систем! Ось чому мені не подобається використовувати термін "вбудований DSL", тому що такі речі, як eLua, можуть бути "DSLs", які "вбудовуються" у два різні сенси, а взагалі не є "вбудованою DSL"!
Щоб гірше було, деякі проекти вводять ще більше плутанини в суміш. Напр. Шаблони Flatiron описуються як "без DSL", хоча насправді це лише ідеальний приклад внутрішнього DSL із синтаксисом:map.where('href').is('/').insert('newurl');
Це було сказано, коли я писав: "JavaScript - це дуже потужна мова, набагато потужніша, ніж CSS (навіть включаючи будь-який з препроцесорів CSS). Це залежить від того, чи віддаєте перевагу внутрішній чи зовнішній DSL для таких речей. Знову ж таки, питання переваги ". Я говорив про ці два сценарії:
Перший:
/** @jsx React.DOM */
var colored = {
color: myColor
};
React.renderComponent(<div style={colored}>Hello World!</div>, mountNode);
Дві:
// SASS:
.colored {
color: $my-color;
}
// HTML:
<div class="colored">Hello World!</div>
Перший приклад використовує те, що було описано в питанні як: "написання CSS в JavaScript. Не дуже". У другому прикладі використовується Sass. Хоча я погоджуюся, що використання JavaScript для написання CSS може бути не дуже (для деяких визначень "досить"), але є одна перевага цього робити.
Я можу мати змінні та функції в Sass, але вони лексично охоплюються чи динамічно охоплюються? Вони набираються статично чи динамічно? Сильно чи слабко? Що з числовими типами? Введіть коерсіон? Які значення є правдивими, а які - хибними? Чи можу я мати функції вищого порядку? Рекурсія? Хвостовий дзвінок? Лексичні закриття? Чи оцінюються вони у звичайному або додатковому порядку? Чи є ледача чи нетерпляча оцінка? Чи передаються аргументи функціям за значенням або посиланням? Вони змінюються? Незмінний? Наполегливі? Що з об’єктами? Заняття? Прототипи? Спадщина?
Це не тривіальні запитання, і все ж я мушу знати відповіді на них, якщо хочу зрозуміти код Сасса чи Менш. Я вже знаю ці відповіді на JavaScript, так що це означає, що я вже розумію всі внутрішні DSL (як стилі вбудованого в React) на цих самих рівнях, тому, якщо я використовую React, я повинен знати лише один набір відповідей на них (і багато подібних ) питання, тоді як коли я використовую напр. Тоді я повинен знати три набори цих відповідей і розуміти їх наслідки.
Це не означає, що так чи інакше завжди краще, але кожен раз, коли ви вводите іншу мову в суміш, тоді ви платите певну ціну, яка може бути не такою очевидною на перший погляд, і ця ціна є складністю.
Я сподіваюся, що я трохи уточнив, що я спочатку мав на увазі.
Прив’язка даних
Двостороння зв'язування
Це дійсно цікавий предмет, а насправді також питання уподобань. Двосторонній не завжди краще, ніж односторонній. Це питання про те, як ви хочете моделювати стан змін у вашій програмі. Я завжди розглядав двосторонні прив'язки як ідею, яка дещо суперечить принципам функціонального програмування, але функціональне програмування - не єдина парадигма, яка працює, деякі люди вважають за краще такий тип поведінки, і обидва підходи, здається, працюють досить добре на практиці. Якщо вас цікавлять деталі дизайнерських рішень, пов’язаних із моделюванням штату в Реакті, тоді дивіться розмову Піта Ханта (пов’язану з цим питанням) та розмову Тома Окчіно та Джордана Уолке, які дуже добре пояснюють це в моя думка.
Оновлення: Дивіться також ще одну розмову Піта Ханта: Будьте передбачувані, невірні: функціональне програмування DOM .
Оновлення 2: Варто відзначити, що багато розробників сперечаються проти двостороннього потоку даних або двостороннього прив'язки, деякі навіть називають це антидіаграмою. Візьмемо для прикладу архітектуру додатків Flux, яка явно уникає моделі MVC (яка виявилася складною для масштабних програм у Facebook та Instagram) на користь суворо однонаправленого потоку даних (див. Hacker Way: Переосмислення розробки веб-додатків у Facebook talk by Том Окчіно, Цзін Чен та Піт Хант для гарного вступу). Також багато критики проти AngularJS (найпопулярніша веб-рамка, яка грунтується на моделі MVC, відома двостороннім зв'язуванням даних), включає аргументи проти цього двостороннього потоку даних, див.
Оновлення 3: Ще одна цікава стаття, яка добре пояснює деякі проблеми, про які йшлося вище, - Деконструкція потоку ReactJS - Не використання MVC з ReactJS Мікаел Брассман, автор RefluxJS (проста бібліотека для однонаправленої архітектури прикладних потоків даних, натхненна Flux).
Оновлення 4: На даний момент Ember.js відходить від двостороннього прив'язки даних, а в майбутніх версіях це буде за замовчуванням односторонній. Дивіться: Розмова про майбутнє Ембер Стефана Пеннера з симпозіуму Ембергартен у Торонто 15 листопада 2014 року.
Оновлення 5: Дивіться також: Дорога до Ембер 2.0 RFC - цікава дискусія у запиті на виклик Тома Дейла :
"Коли ми розробили оригінальний шар шаблону, ми зрозуміли, що зробити прив'язку даних двостороннім не дуже шкідливо: якщо ви не встановите двосторонню прив'язку, це фактично одностороння прив'язка!
З тих пір ми зрозуміли (за деякою допомогою наших друзів з React), що компоненти хочуть мати можливість передавати свої дані своїм дітям, не будучи на варті, коли вони мутації.
Крім того, спілкування між компонентами найчастіше виражається як події чи зворотні дзвінки . Це можливо в Ембер, але домінування двосторонніх зв'язків даних часто призводить людей до шляху використання двосторонніх прив'язок як каналу зв'язку . Досвідчені розробники Ембер (як правило) не роблять цієї помилки, але це зробити легко. " [Наголос додано]
Рідні проти В.М.
Підтримка власного браузера (читайте "гарантовано швидше")
Тепер нарешті щось, що не є питанням думки.
Насправді тут все навпаки. Звичайно, "рідний" код можна записати на C ++, але як ви думаєте, на чому написані двигуни JavaScript?
Насправді двигуни JavaScript справді вражають оптимізаціями, якими вони користуються сьогодні - і не тільки V8 більше, але SpiderMonkey і навіть Чакра сяє в наші дні. І майте на увазі, що з компіляторів JIT код не тільки настільний, наскільки це можливо, але є також можливості оптимізації часу роботи, які просто неможливо зробити в будь-якому статично складеному коді.
Коли люди думають, що JavaScript повільний, вони зазвичай мають на увазі JavaScript, який отримує доступ до DOM. DOM повільний. Це рідний текст, написаний на C ++, але все ж він повільний, як пекло через складність, яку він має реалізувати.
Відкрийте консоль і напишіть:
console.dir(document.createElement('div'));
і подивіться, скільки властивостей div
має реалізувати порожній елемент, який навіть не приєднаний до DOM. Це лише властивості першого рівня, які є "власними властивостями", тобто. не успадковані від прототипу ланцюга:
вирівнювати, onwaiting, onvolumechange, ontimeupdate, onsuspend, onsubmit, onstalled, OnShow, onselect, onseeking, onseeked, OnScroll, OnResize, OnReset, onratechange, OnProgress, onplaying, onplay, OnPause, OnMouseWheel, OnMouseUp, OnMouseOver, onmouseout, OnMouseMove, OnMouseLeave, onmouseenter, onmousedown, onloadstart, onloadedmetadata, onloadeddata, onload, onkeyup, onkeypress, onkeydown, oninvalid, oninput, onfocus, onerror, onended, onemptied, ondurationchange, ondrop, ondragstart, ondragover, ondraterndra, ondragendra, ondraterndra oncontextmenu, OnClose, OnClick, OnChange, oncanplaythrough, oncanplay, OnCancel, ONBLUR, OnAbort, перевірка орфографії, isContentEditable, contentEditable, outerText, InnerText, Accesskey, прихований, webkitdropzone, перетягувати, TabIndex, реж, переклад, мови, назва, childElementCount, lastElementChild,firstElementChild, діти, nextElementSibling, попереднійElementSibling, onwheel, onwebkitfullscreenerror, onwebkitfullscreenchange, onselectstart, onsearch, onpaste, oncut, oncopy, onbeforepaste, onbeforecut, onbeforext, sclesh, scll, sckll, sckll, webkit, webkit, webkit, webkit, webkit, webkit, webkit, webkit, webkit, webkit, webkit, sckll, sckll, webkit, sckll, webkit, sckll, webkit, sckll, webkit, sckll, webkit, sckll, webkit, sckst clientHeight, clientWidth, clientTop, clientLeft, offsetParent, offsetHeight, offsetWidth, offsetTop, offsetLeft, localName, префікс, простір іменURI, id, стиль, атрибути, тегName, parentElement, textContent, baseURI, ownerDocument, nextSiblingC lasth, firstSiblingC lasth, firstsiildCild, lastHiildCild parentNode, nodeType, nodeValue, nodeNameoncopy, onbeforepaste, onbeforecut, onbeforecopy, webkitShadowRoot, набір даних, classList, className, externalHTML, innerHTML, scrollHeight, scrollWidth, scrollTop, scrollLeft, clientHeight, clientWidth, clientTop, clientLeft, offseteset, offset, offset, offset, offset, offset, offset простір іменURI, id, стиль, атрибути, tagName, parentElement, textContent, baseURI, ownerDocument, nextSibling, previousSibling, lastChild, firstChild, childNodes, parentNode, nodeType, nodeValue, nodeNameoncopy, onbeforepaste, onbeforecut, onbeforecopy, webkitShadowRoot, набір даних, classList, className, externalHTML, innerHTML, scrollHeight, scrollWidth, scrollTop, scrollLeft, clientHeight, clientWidth, clientTop, clientLeft, offseteset, offset, offset, offset, offset, offset, offset простір іменURI, id, стиль, атрибути, 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
Багато з них насправді є вкладеними об’єктами - щоб побачити (власні) властивості другого рівня порожнього корінця div
у вашому браузері, дивіться цю загадку .
Я маю в виду серйозно, onvolumechange власності на кожному окремому вузлі сну? Це помилка? Ні, це лише застаріла версія традиційної події DOM рівня 0 одного з обробників подій, "яка повинна підтримуватися всіма елементами HTML , як атрибутами вмісту, так і атрибутами IDL" [наголос додано] в Розділі 6.1.6.2 специфікації HTML від W3C - не обійти.
Тим часом, це властивості першого рівня фальшивої DOM div
в React:
реквізит, _власник, _lifeCycleState, _pendingProps, _pendingCallbacks, _pendingOwner
Цілком різниця, чи не так? Насправді це весь об’єкт, серіалізований у JSON ( LIVE DEMO ), адже так, насправді ви можете його серіалізувати до JSON, оскільки він не містить жодних кругових посилань - щось немислиме у світі рідного DOM ( де це просто кине виняток ):
{
"props": {},
"_owner": null,
"_lifeCycleState": "UNMOUNTED",
"_pendingProps": null,
"_pendingCallbacks": null,
"_pendingOwner": null
}
Це, головним чином, основна причина, чому React може бути швидшим, ніж рідний веб-переглядач DOM - тому що він не повинен реалізувати цей безлад .
Перегляньте цю презентацію Стівена Люшера, щоб побачити, що швидше: рідний DOM, написаний на C ++ або підроблений DOM, повністю написаний на JavaScript. Це дуже чесна та захоплююча презентація.
Оновлення: Ember.js у майбутніх версіях використовуватиме віртуальний DOM, натхненний React для покращення продуктивності. Дивіться: Розмова про майбутнє Ембер Стефана Пеннера з симпозіуму Ембергартен у Торонто 15 листопада 2014 року.
Підсумовуючи це: такі функції веб-компонентів, як шаблони, прив'язка даних або користувацькі елементи матимуть багато переваг перед React, але поки сама модель об'єкта документа не буде значно спрощена, продуктивність не буде однією з них.
Оновлення
Через два місяці після того, як я опублікував цю відповідь, тут з’явились новини, які є актуальними Як я щойно писав у Twitter , найновіша версія редактора тексту Atom, написана GitHub на JavaScript, використовує Facebook React для кращої продуктивності, хоча згідно Вікіпедії "Atom заснований на Chromium та написаний на C ++", тому він має повний контроль над нативної реалізації C ++ DOM (див. «Ядро Атома» ) та гарантовано матиме підтримку веб-компонентів, оскільки вона постачається разом із власним веб-браузером. Це лише нещодавній приклад проекту в реальному світі, який міг би використовувати будь-який інший тип оптимізації, як правило, недоступний для веб-додатків, але все ж він вирішив використовувати React, який сам написаний на JavaScript, для досягнення найкращої продуктивності, хоча Atom не було побудовано з React для початку, тож це не було дрібницею.
Оновлення 2
Існує цікаве порівняння Todd Parker, використовуючи WebPagetest для порівняння продуктивності прикладів TodoMVC, написаних у Angular, Backbone, Ember, Polymer, CanJS, YUI, Knockout, React та Shoestring. Це найбільш об'єктивне порівняння, яке я бачив досі. Тут важливо те, що всі відповідні приклади були написані експертами у всіх цих рамках, всі вони доступні на GitHub і їх можна вдосконалити будь-хто, хто думає, що деякий код можна оптимізувати для швидшого запуску.
Оновлення 3
У наступних версіях Ember.js буде включати ряд особливостей React, про які йдеться тут (включаючи віртуальний DOM та односпрямоване прив'язування даних, якщо назвати лише декілька), що означає, що ідеї, що виникли в React, вже мігрують в інші рамки. Див.: Дорога до Ембер 2.0 RFC - цікаве обговорення у запиті на виклик Тома Дейла (дата початку: 2014-12-03): "У Ember 2.0 ми будемо приймати" віртуальну DOM "та модель потоку даних, яка охоплює найкращі ідеї від React та спрощує спілкування між компонентами. "
Крім того, Angular.js 2.0 реалізує багато концепцій, обговорених тут.
Оновлення 4
Я маю детально розглянути кілька питань, щоб відповісти на цей коментар Ігве Калу:
"нерозумно порівнювати React (JSX або вихід компіляції) з простим JavaScript, коли React в кінцевому рахунку зводиться до простого JavaScript. [...] Яку б стратегію React не використовував для вставки DOM, можна застосувати без використання React. не додає жодних особливих переваг при розгляді розглянутої функції, крім зручності. " (повний коментар тут )
У випадку, якщо це було недостатньо зрозуміло, в частині своєї відповіді я порівнюю ефективність роботи безпосередньо на рідному DOM (реалізованому як хост-об’єкти в браузері) з підробленим / віртуальним DOM React (реалізованим у JavaScript). Справа в тому що я намагався зробити це , що віртуальний DOM реалізований в JavaScript може випереджати реальний DOM , реалізований в C ++ і НЕ що React може випереджати JavaScript (який , очевидно , не має особливого сенсу , так як це буде написано в JavaScript). Моя думка полягала в тому, що "рідний" код C ++ не завжди гарантується швидше, ніж "неродний" JavaScript. Використання React для ілюстрації цієї точки було лише прикладом.
Але цей коментар торкнувся цікавого питання. У певному сенсі це правда, що вам не потрібні будь-які рамки (React, Angular або jQuery) з будь-якої причини (як-от продуктивність, портативність, функції), тому що ви завжди можете відтворити, що рамка робить для вас, і винаходити колесо - якщо ви можете виправдати витрати, тобто.
Але - як добре сказав Дэйв Сміт: Як пропустити крапку при порівнянні продуктивності веб-фрейму : "Порівнюючи дві веб-рамки, питання полягає в тому, чи не може мій додаток бути швидким з фреймворком X? Питання в тому, чи буде мій додаток швидким із фреймворком X. "
У своїй відповіді 2011 року на те: Які емпіричні технічні причини не використовувати jQuery, я пояснюю подібну проблему, що неможливо написати портативний DOM-маніпуляційний код без бібліотеки, як jQuery, але що люди рідко роблять це.
Під час використання мов програмування, бібліотек або фреймворків люди, як правило, використовують найзручніші або ідіоматичні способи ведення справ, не ідеальні, а незручні. Справжня цінність хороших рамок полегшує те, що інакше було б важко зробити - і секрет - зробити правильні речі зручними. Результат все ще має абсолютно таку ж потужність у вашому розпорядженні, як найпростіша форма обчислення лямбда або найпримітивніша машина Тьюрінга, але відносна виразність певних понять означає, що саме ці поняття мають тенденцію висловлюватися легше або зовсім, і що правильні рішення не просто можливі, але реально реалізуються широко.
Оновлення 5
Реагувати + продуктивність =? стаття Пола Льюїса з липня 2015 року показує приклад, коли React повільніше, ніж ванільний JavaScript, написаний від руки для нескінченного списку зображень Flickr, що особливо важливо для мобільних пристроїв. Цей приклад показує, що кожен завжди повинен перевірити продуктивність для конкретного випадку використання та конкретних цільових платформ та пристроїв.
Дякую Кевіну Лозанд'є за те, що він звернув це до мене .