Обновление: этот ответ кажется довольно популярным, поэтому я потратил некоторое время, чтобы немного его очистить, добавить новую информацию и прояснить несколько вещей, которые, на мой взгляд, были недостаточно ясными. Пожалуйста, прокомментируйте, если вы считаете, что что-то еще требует уточнения или обновления.
Большинство ваших проблем на самом деле зависит от мнений и личных предпочтений, но я постараюсь ответить максимально объективно:
Родной или скомпилированный
Пишите JavaScript в vanilla 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, могут быть «DSL», которые «встроены» в двух разных смыслах, но вовсе не являются «встроенными 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, но имеют ли они лексическую или динамическую область? Они статически или динамически типизированы? Сильно или слабо? Как насчет числовых типов? Типа принуждения? Какие ценности являются правдивыми, а какие ложными? Могу ли я иметь функции высшего порядка? Рекурсия? Хвостовые звонки? Лексические затворы? Оцениваются ли они в обычном или аппликативном порядке? Есть ли ленивая или нетерпеливая оценка? Аргументы функций передаются по значению или по ссылке? Они изменчивы? Неизменный? Стойкие? Как насчет объектов? Классы? Прототипы? Наследование?
Это не тривиальные вопросы, и все же я должен знать ответы на них, если я хочу понять код Sass or Less. Я уже знаю эти ответы для JavaScript, так что это означает, что я уже понимаю каждый внутренний DSL (например, встроенные стили в React) на этих самых уровнях, поэтому, если я использую React, мне нужно знать только один набор ответов на них (и многие похожие ) вопросы, а когда я использую для например. Sass и Handlebars, тогда я должен знать три набора этих ответов и понять их значение.
Нельзя сказать, что так или иначе всегда лучше, но каждый раз, когда вы вводите в микс другой язык, вы платите какую-то цену, которая может быть не столь очевидной на первый взгляд, и эта цена сложна.
Надеюсь, я немного разъяснил, что я имел в виду.
Привязка данных
Двухстороннее связывание
Это действительно интересная тема, а на самом деле также вопрос предпочтений. Двусторонняя не всегда лучше односторонней. Вопрос в том, как вы хотите смоделировать изменяемое состояние в вашем приложении. Я всегда рассматривал двусторонние привязки как идею, несколько противоречащую принципам функционального программирования, но функциональное программирование - не единственная парадигма, которая работает, некоторые люди предпочитают такой тип поведения, и оба подхода на практике работают довольно хорошо. Если вам интересны подробности проектных решений, связанных с моделированием состояния в React, посмотрите доклад Пита Ханта (связанный с этим вопросом) и доклад Тома Оккино и Джордана Уолка, которые очень хорошо объясняют это в мое мнение.
Обновление: см. Также другой доклад Пита Ханта: « Будь предсказуемым, а не правильным: функциональное программирование DOM» .
Обновление 2: Стоит отметить, что многие разработчики выступают против двунаправленного потока данных или двусторонней привязки, некоторые даже называют это анти-паттерном. Возьмем, к примеру, архитектуру приложений Flux, которая явно избегает модели MVC (которую трудно масштабировать для больших приложений Facebook и Instagram) в пользу строго однонаправленного потока данных (см. Путь Хакера: переосмысление разработки веб-приложений на Facebook , Том Оккино, Цзин Чен и Пит Хант за хорошее представление). Кроме того, много критики против AngularJS (самая популярная веб-инфраструктура, в основе которой лежит модель MVC, известная как двусторонняя привязка данных), включает аргументы против этого двунаправленного потока данных, см .:
Обновление 3: Еще одна интересная статья, которая хорошо объясняет некоторые из обсуждаемых выше проблем, - это деконструкция потока ReactJS - не использовать MVC с ReactJS от Микаэля Брассмана, автора RefluxJS (простой библиотеки для однонаправленной архитектуры приложений потока данных, вдохновленной Flux).
Обновление 4: Ember.js в настоящее время удаляется от двусторонней привязки данных, и в будущих версиях он будет односторонним по умолчанию. См .: «Будущее Эмбер», выступление Стефана Пеннера на симпозиуме «Эмбергартен» в Торонто 15 ноября 2014 года.
Обновление 5: См. Также: «Дорога к Ember 2.0 RFC» - интересное обсуждение в запросе на извлечение Тома Дейла :
«Когда мы разрабатывали оригинальный шаблонный слой, мы полагали, что создание двухсторонних привязок данных не очень вредно: если вы не установите двустороннюю привязку, это де-факто односторонняя привязка!
С тех пор мы поняли (с некоторой помощью наших друзей из React), что компоненты хотят иметь возможность передавать данные своим детям, не заботясь о своенравных мутациях.
Кроме того, связь между компонентами часто наиболее естественно выражается в виде событий или обратных вызовов . Это возможно в Ember, но доминирование двусторонних привязок данных часто приводит людей к пути использования двусторонних привязок в качестве канала связи . Опытные разработчики Ember (как правило) не делают эту ошибку, но ее легко сделать ». [Выделение добавлено]
Родной против ВМ
Встроенная поддержка браузера (читайте "гарантированно быстрее")
Теперь наконец то, что не является вопросом мнения.
На самом деле здесь все наоборот. Конечно, «родной» код может быть написан на C ++, но как вы думаете, на чем написаны движки JavaScript?
На самом деле движки JavaScript поистине удивительны в тех оптимизациях, которые они используют сегодня - и уже не только V8, но и SpiderMonkey и даже Chakra в наши дни. И имейте в виду, что с JIT-компиляторами код не только настолько естественный, насколько это возможно, но также есть возможности оптимизации во время выполнения, которые просто невозможно сделать в любом статически скомпилированном коде.
Когда люди думают, что JavaScript медленный, они обычно имеют в виду JavaScript, который обращается к DOM. DOM медленный. Это нативный код, написанный на C ++, и все же он медленный из-за сложности, которую он должен реализовать.
Откройте консоль и напишите:
console.dir(document.createElement('div'));
и посмотрите, сколько свойств должен реализовать пустой div
элемент, который даже не присоединен к DOM. Это только свойства первого уровня, которые являются «собственными свойствами», т.е. не наследуется от прототипа цепочки:
выравнивание, ожидание, onvolumechange, ontimeupdate, onsuspend, onsmit, onstald, onshow, onselect, onking onmouseenter, onmousedown, onloadstart, onloadedmetadata, onloadeddata, onload, onkeyup, onkeypress, onkeydown, oninvalid, oninput, onfocus, onerror, onended, односторонний, ondurationchange, ondrop, ondragstart, ondragover, ondrag oncontextmenu, OnClose, OnClick, OnChange, oncanplaythrough, oncanplay, OnCancel, ONBLUR, OnAbort, проверка орфографии, isContentEditable, contentEditable, outerText, InnerText, Accesskey, скрытый, webkitdropzone, перетаскивать, TabIndex, реж, перевод, языки, название, childElementCount, lastElementChild,firstElementChild, дети, nextElementSibling, previousElementSibling, onwheel, onwebkitfullscreenerror, onwebkitfullscreenchange, onselectstart, onsearch, onpaste, oncut, oncopy, onbeforepaste, onbeforecut, onbeforecopy, webkitShadowRoot, набор данных, ClassList, имя класса, outerHTML, innerHTML, scrollHeight, scrollWidth, scrollTop, scrollLeft, clientHeight, clientWidth, clientTop, clientLeft, offsetParent, offsetHeight, offsetWidth, offsetTop, offsetLeft, localName, префикс, namespaceURI, идентификатор, стиль, атрибуты, tagName, parentElement, textContent, baseURI, ownerDocument, nextSibling, previousSibling, previousSibling, firstSibling, lastChildChildChildChildC parentNode, nodeType, nodeValue, nodeNameoncopy, onbeforepaste, onbeforecut, onbeforecopy, webkitShadowRoot, набор данных, classList, className, outerHTML, innerHTML, scrollHeight, scrollWidth, scrollTop, scrollLeft, clientHeight, clientWidth, clientWidth, смещение, смещение, смещение, смещение, выкл namespaceURI, id, стиль, атрибуты, tagName, parentElement, textContent, baseURI, ownerDocument, nextSibling, previousSibling, lastChild, firstChild, childNodes, parentNode, nodeType, nodeValue, nodeNameoncopy, onbeforepaste, onbeforecut, onbeforecopy, webkitShadowRoot, набор данных, classList, className, outerHTML, innerHTML, scrollHeight, scrollWidth, scrollTop, scrollLeft, clientHeight, clientWidth, clientWidth, смещение, смещение, смещение, смещение, выкл namespaceURI, 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 на каждом узле div? Это ошибка? Нет, это просто традиционная версия модели событий DOM Level 0 одного из обработчиков событий, «которая должна поддерживаться всеми элементами HTML , как атрибутами содержимого, так и атрибутами IDL» [выделение добавлено] в Разделе 6.1.6.2 спецификации HTML. по W3C - никак не обойтись.
Между тем, это свойства первого уровня поддельного DOM div
в React:
props, _owner, _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, использует React Facebook для повышения производительности, даже если согласно Википедии «Atom основан на Chromium и написан на C ++», поэтому он имеет полный контроль над нативная реализация C ++ DOM (см . Ядро Atom ) и гарантированно будет иметь поддержку веб-компонентов, так как он поставляется с собственным веб-браузером. Это всего лишь недавний пример реального проекта, который мог бы использовать любой другой вид оптимизации, обычно недоступный для веб-приложений, и все же он решил использовать React, который сам написан на JavaScript, для достижения максимальной производительности, даже при том, что Atom не был построен с React с самого начала, поэтому это не было тривиальным изменением.
Обновление 2
У Тодда Паркера есть интересное сравнение с использованием WebPagetest для сравнения производительности примеров TodoMVC, написанных на Angular, Backbone, Ember, Polymer, CanJS, YUI, Knockout, React и Shoestring. Это самое объективное сравнение, которое я видел до сих пор. Здесь важно то, что все соответствующие примеры были написаны экспертами во всех этих средах, все они доступны на GitHub и могут быть улучшены любым, кто считает, что часть кода может быть оптимизирована для более быстрой работы.
Обновление 3
Ember.js в будущих версиях будет включать в себя ряд функций React, которые обсуждаются здесь (включая виртуальную DOM и однонаправленную привязку данных, и многие другие), что означает, что идеи, которые возникли в React, уже переносятся в другие среды. См .: Дорога к Ember 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. Вопрос в том, будет ли мое приложение быстрым с фреймворком ИКС."
В своем ответе от 2011 года: по каким-то эмпирическим техническим причинам не использовать jQuery я объясняю аналогичную проблему: невозможно написать переносимый код для манипулирования DOM без такой библиотеки, как jQuery, но люди редко делают это.
При использовании языков программирования, библиотек или фреймворков люди склонны использовать самые удобные или идиоматические способы ведения дел, а не совершенные, но неудобные. Истинная ценность хороших фреймворков заключается в том, чтобы упростить то, что в противном случае было бы трудно сделать, и секрет в том, чтобы сделать правильные вещи удобными. В результате в вашем распоряжении все та же сила, что и у простейшей формы лямбда-исчисления или самой примитивной машины Тьюринга, но относительная выразительность некоторых понятий означает, что эти самые понятия имеют тенденцию выражаться легче или вообще, и что правильные решения не только возможны, но и широко применяются.
Обновление 5
Реагировать + Производительность =? В статье Пола Льюиса от июля 2015 года приведен пример, в котором React медленнее, чем ванильный JavaScript, написанный вручную для бесконечного списка картинок Flickr, что особенно важно для мобильных устройств. Этот пример показывает, что каждый должен всегда тестировать производительность для конкретного варианта использования и конкретных целевых платформ и устройств.
Спасибо Кевину Лозандье за то, что он привлек мое внимание .