Привет!
Мы в Хекслете любим свой стек технологий :) Когда рассказываем другим — многие завидуют: Rails, Docker, AWS, React JS. Реакт мы используем в первую очередь для создания веб-среды для разработки Hexlet-IDE, которая позволяет нашим пользователям выполнять упражнение по разработке приложений и взаимодействию с виртуальной машиной прямо в браузере.
Сегодня мы публикуем перевод статьи «An Unconventional Review of React» Джеймса Шора, ведущего проекта Let’s Code: Test-Driven JavaScript.
Он мне понравился. Я не ожидал такого.
Для специальных выпусков Let’s Code JavaScript в августе и сентябре я изучал Реакт.
На случай если вам не знаком Реакт: это библиотека для фронт-энд веб-разработки. С помощью него создаются компоненты: короткие, не-совсем-ХТМЛ теги, которые можно комбинировать для создания интерфейса.
Реакт знаменит своими нетрадиционными решениями: реализацией виртуального DOM’а, созданием элементов интерфейса в JavaScript вместо шаблонов, создание суперсета языка JavaScript — JSX, который позволяет вставлять не-совсем-ХТМЛ прямо в код JS.
С этими решениями разработка уходит от схемы манипуляции DOM’ом — добавить этот элемент, скрыть эффектом другой элемент, обновить это текстовое поле. Вместо этого вы описываете как сейчас должен выглядеть DOM. Реакт берет на себя сложную работу по определению необходимых действий чтобы DOM на самом деле стал выглядеть так, как вы сказали.
Например, в коде который я написал для обзора есть таблица, которая должна меняться каждый раз когда изменяются поля конфигурации. Можно подумать что код будет полон страшной логики по манипуляции DOM, но на самом деле никакого кода для манипуляции нет вообще. Вот он:
/** @jsx React.DOM */
// Copyright (c) 2014 Titanium I.T. LLC. All rights reserved. For license, see "README" or "LICENSE" file.
"use strict";
var StockMarketTableCell = require("./stock_market_table_cell.js");
var StockMarketTableRow = module.exports = React.createClass({
render: function render() {
var year = this.props.stockMarketYear;
return <tr>
<StockMarketTableCell value={year.year()} />
<StockMarketTableCell value={year.startingBalance()} />
<StockMarketTableCell value={year.startingCostBasis()} />
<StockMarketTableCell value={year.totalSellOrders().flipSign()} />
<StockMarketTableCell value={year.capitalGainsTaxIncurred().flipSign()} />
<StockMarketTableCell value={year.growth()} />
<StockMarketTableCell value={year.endingBalance()} />
</tr>;
}
});
В этом суть Реакта, и этим он отличается от других. Единственный вопрос: хорош ли он?
Критерии нетрадиционного обзора
Обычно, когда вы читаете обзор фронт-энд фреймворка или библиотеки, вы узнаете о его размере, или о том, какие знаменитые люди и компании используют его, или о его производительности. Да, все это важно. Но самый важный вопрос для меня звучит проще:
В течение следующих 5-10+ лет, когда я буду поддерживать свой продукт, этот код принесет больше пользы или страданий?
Не так важно сколько времени библиотека сэкономит мне при начальной разработке. О, нет. Намного важнее стоимость поддержки в течение жизни моего приложения.
Для этого у меня есть пять критериев.
1. Замкнутость (Lock-in). Когда я решу перейти на новый или более лучший фреймворк (или библиотеку), насколько сложно будет переключиться?
2. Упрямая архитектура (Opinionated Architecture). Могу ли я решать задачи так, как нужно моему приложению, или я должен повиноваться неким идеям, разработанным авторами фреймворка?
3. Побочная сложность (Accidental Complexity). Я трачу время на решение своей проблемы или борюсь с фреймворком?
4. Тестирование (Testability). Могу ли я просто тестировать свой код, без лишней мороки с mock-объектами?
5. Совместимость с поисковыми движками (Search Engine Compatibility). Придется ли танцевать с бубном чтобы заставить поисковики индексировать мой сайт?
Я оценил Реакт в каждой категории с помощью a ☺ (уиии!), ☹ (буэээ!), or ⚇ (ни туда, ни сюда).
1. Замкнутость — ⚇ (ни туда, ни сюда)
Давайте начистоту: когда вы решите попрощаться с Реактом, вам придется переписывать интерфейс с нуля. Нет нормального способа скрыть Реакт за слоем абстракции, и код Реакт-интерфейса не похож ни на что другое. Так что все довольно замкнуто.
Но почему Реакт не получил печальную рожицу?
Два момента спасают Реакт. Во-первых, Реакт провоцирует помещать логику приложения вне UI, так что не придется переписывать все приложение.
Во-вторых, API Реакта относительно небольшое. Не так много точек соприкосновения (см. пункт 3). Это означает, что меньше вероятность сломать что-то при обновлении Реакта. Также, Реакт легко использовать изолированно, для отдельной части страницы, так что можно постепенно мигрировать с Реакта когда появится необходимость.
2.Упрямая архитектура — ☺ (уиии!)
Реакт это библиотека, а не фреймворк, и это заметно. Реакт не диктует архитектуру приложения. У меня не было никаких проблем по подключению моего существующего и заведомо странного кода к Реакту.
Некоторые люди считают упрямую архитектуру хорошим признаком. «Это позволяет мне понять как структурировать код». Я предпочитаю обратное. Структура приложения должна диктоваться требованиями приложения. Фреймворк не может предсказать эти требования, и они будут меняться с развитием приложения.
Реакт содержит архитектурный паттерн под названием Flux, но он полностью опционален. Это просто способ рассуждать о структуре кода, а не встроенный в Реакт механизм. Так и должно быть.
3. Побочная сложность — ⚇ (ни туда, ни сюда)
О Реакте необходимо знать очень мало, и он мне показался простым и понятным. Есть лишь пара моментов (разница между «props» и «state»), лишь пара концепций (как управлять состоянием; иммутабельные рендер-методы) и несколько методов для реализации типичного компонента. Мой самый сложный компонент содержит аж три Реакт-метода. В большинстве случаев хватает одного render().
Ложка дегтя это виртуальный DOM. Это впечатляющее достижение, ключевая штука в Реакте… но он может оказаться дырявой абстракцией. Как только вы видите новую или продвинутую фичу DOM, вы рискуете напороться на драку с Реактом. Например, CSS анимации некоторое время вызывали проблемы, и управление фокусом все еще шалит.
Сейчас, кажется, команда Реакта успевает все править. Но что будет через пять или десять лет, когда Реакт уже не самая модная штука? Браузера продолжают развиваться, и перед тем как выбрать Реакт для своего проекта, спросите себя: уверен ли я, что Реакт будет поспевать за всеми нововведениями, необходимыми моему приложению?
4. Тестирование — ☺ (уиии!)
История с тестированием Реакта выглядит немного незрелой. Она еле-еле дотянула до улыбающейся рожицы.
На первый взгляд Реакт дает простой API для тестирования, и кажется там есть все что нужно. Можно рендерить компонент и искать в дереве DOM с помощью хорошего набора функций. Можно симулировать события. Можно делать mock-компоненты, но я был доволен тем, что они мне ни разу не понадобились.
Документации не очень много, не хватает примеров и рекомендаций по дизайну, особенно если сравнить с остальными частями документации Реакта. Это только поддерживает ощущение незрелости. Но главная проблема в том, что я не смог найти способа сравнивать компоненты. Даже в сравнительно простом приложении, ваши компоненты будут содержать компоненты, и вы не хотите чтобы тесты знали что-то о деталях реализации дополнительных компонентов.
Например, компонент ApplicationUi содержит компонент StockMarketTable. Я хочу проверить обновляется ли таблица при изменении конфигурации. Для этого я хотел проверить таблицу с готовым хард-кодом:
it("updates stock market table when user configuration changes", function() {
config.setStartingBalance(...);
var expectedTable = <StockMarketTable stockMarketProjection={projectionFor(config)} />;
var actualTable = TestUtils.findRenderedComponentWithType(app, StockMarketTable);
// но как делать сравнение?
});
В итоге я обошел проблему, закопавшись в приватные детали реализации Реката, рендерил компоненты в статичный ХТМЛ и сравнивал результат. Вот код.
function checkComponent(actual, expected) {
var actualRendering = React.renderComponentToStaticMarkup(actual._descriptor);
var expectedRendering = React.renderComponentToStaticMarkup(expected);
expect(actualRendering).to.equal(expectedRendering);
}
Он работает, но в случае падения генерирует страшное сообщение об ошибке, и этот код зависит от деталей реализации Реакта. (Пришлось визуализировать объектный граф рантайма!) Возможно есть способ лучше — и должен быть способ лучше — но я его не нашел.
Кроме этой не такой уж мелкой проблемы, тестировать Реакт одно удовольствие. Зацените тесты ApplicationUi. Они чудесны в своей простоте, понятны и не содержать mock’ов. Ни один тест не содержит больше двух строк, и это самый сложный компонент всего приложения.
5. Совместимость с поисковыми движками — ☺ (уиии!)
Реакту удалось сделать небольшое чудо: они сделали фронт-энд фреймворк, не имеющий проблем в поисковиками.
Благодаря виртуальному DOM’у, Реакт-приложения могут рендериться на стороне сервера с помощью Node.js. Это означает что нужен лишь один канал рендера и для поисковиков и для реальных пользователей. Это также означает что страницы показываются сразу после их подачи. Никакого ожидания document ready событий или загрузки всего JS. Реакт можно использовать для подачи статичной разметки, без клиентского кода вообще.
Круто.
Итог: рекомендую.
Реакт на удивление хорошо собранная библиотека. Мне он показался простым, интуитивно-понятным и легким в использовании. Если вы ищете фронт-энд библиотеку для сложных приложений или даже для сложных виджетов для существующей страницы, стоит обратить внимание на Реакт. Он не идеален, и стоит задуматься о потенциальных проблемах с дырявой абстракцией DOM’а, но он очень хорош. Рекомендую.
Автор: freetonik