[Перевод] Год использования ReactJS: подводим итоги
Почти год прошел, как мы начали применять ReactJS и переваривать море сопутствующих технологий. И мы здесь, чтобы поделиться с вами своим опытом.
Недавно мы закончили проект, который использовал рендеринг на стороне клиента и API сервер на Rails. Общее впечатление — глубоко положительное.
Что нам понравилось:
- Хорошие и понятные исходники. Декларативный стиль React позволяет легко понимать код и избегать ненужной сложности. Которая так и норовит скопиться в UI.
- Явное разграничение на серверный и клиентский код. Можно легко разделять задачи между фронтенд и бекенд разработчиками, и они не будут друг с другом пересекаться.
- Благодаря технологии Virtual DOM изменения страницы происходят очень быстро (за исключением вырожденных случаев вроде игр или чего-то столь же интерактивного). Но стоит заметить, что в последнее время мы часто встречаем статьи (например, вот эта), авторы которых утверждают, что React медленный.
- Если вы хотите открыть API наружу, то разработка SPA приложения для него будет лучшим из возможных тестов. Причем код, который взаимодействует с бекендом, можно вынести в один или несколько отдельных модулей: это намного лучше, чем хардкодить AJAX запросы в Flux. К примеру, модуль для работы с настройками может делать разные запросы в зависимости от того, запущено приложение на проде или на стейдже.
- Большое количество разных реализаций Flux. Хорошо, если вы любите держать руку на пульсе технологий. Плохо, если просто хотите быстро что-то сделать и не знаете, что выбрать. Некоторые реализации используют несколько «сторов», другие — один глобальный объект с FRP или эвентами для координирования компонентов. Всегда можно выбрать то, что больше нравится. Мы выбрали «Fluxxor», но не используем его реализацию сторов, только actions. Для работы с состоянием мы выбрали подход с одним глобальным объектом и используем библиотеку «Baobab». Со стороны выглядит как переусложнение, но нам в компании нравится гибкость.
Естественно, в этом новом прекрасном мире не все так прекрасно. С рядом проблем мы все же столкнулись:
- Недостаток хорошо поддерживаемых, готовых к использованию компонентов. Конечно, это вопрос курицы и яйца — их количество увеличится вместе с ростом популярности самого React. Но мы-то живем здесь и сейчас.
- Формы и валидация. Их сложно делать. Плюс готовых высокоуровневых библиотек для создания форм не то чтобы много.
- Для своего приложения мы использовали кастомную тему с большим количеством кода jQuery. Оказалось, что её не так просто бесшовно интегрировать в React приложение! Технически возможно, но я бы не рекомендовал заниматься этим, если у вас есть уже готовые компонент или ресурсы, чтобы такой сделать с нуля. В нашем случае был нужен date time picker на bootstrap, и мы не нашли хорошего готового (этот не обладал всеми нужными нам функциями). Так что у нас не оставалось выбора, кроме как оформить подходящий jQuery плагин в виде компонента React. Если вам интересна эта тема, то вы можете прочитать больше здесь.
Несколько технических решений, которые нам кажутся правильными и которые мы хотим рекомендовать:
- Используйте один стор. И вот почему. Во-первых, основная проблема с несколькими сторами — их синхронизация. Если вам нужны данные из одного стора, для которых нужны данные из другого стора, вам придется прописывать это в явном виде с помощью функции waitFor (). С ростом приложения таких связей будет все больше и вам будет все труднее ими управлять. Использование одного стора для управления состояниям решает подобные проблемы. Хорошим кандидатом для такого стора является реализация из библиотеке «Baobab». Можете рассматривать его как разновидность локальной базы данных, которая живет на фронтенде.
- Используйте webpack для сборки модулей. Сейчас для такой сборки есть множество решений, к примеру, grunt или gulp совместно с browserify или requirejs, но webpack немного мощнее. Прямое включение css, картинок и шрифтов в React компоненты позволяет сделать css более управляемым.
- Используйте курсоры, чтобы передавать данные React компонентам. Эта мощная абстракция позволяет не передавать промежуточные данные вниз по «цепочке» компонентов, что сильно упрощает код. Мы использовали курсоры из Baobab, но есть много других хороших реализаций. Baobab был выбран из-за его простоты, прямолинейности и готовой интеграции с React, поддерживающей PureRenderMixin
Что мы хотим попробовать в следующем приложении:
Использовать Babel
Babel уже официально используется React для работы с JSX. Но, даже если вы не используете JSX, Babel все равно является великолепным инструментом для фронтенд разработки. Кто не хочет пользоваться всеми этими крутыми возможностями ES6 и ES7, не дожидаясь их поддержки в браузерах.
Изоморфные приложения
Прямо сейчас это тренд. Если еще не слышали о таком подходе, вот хороший обзор от команды Airbnb. Идея состоит в том, чтобы отрендерить HTML страницу на сервере, передать ее на клиент (с кешированием и CDN) чтобы она мгновенно отобразилась в браузере, а затем, когда загрузиться JavaScript, ReactJS «подцепится» к уже отрендеренной странице и приложение «оживет».
Использовать Functional Reactive Programming (FRP) для координации (как диспатчер Flux)
Есть несколько хороших библиотек для использования FRP в JavaScript (RxJS, Bacon, Kefir). Основная идея в том, чтобы представить переменные как последовательность их изменений, и комбинировать эти изменения с помощью таких функций как map, filter, reduce и функций высшего порядка (функция, которая принимает другую функцию в качестве аргумента). Для пользовательского интерфейса такой подход дает возможность преобразовать последовательность эвентов в поток эвентов и манипулировать такими потоками как объектами.
Один стор для управления состоянием
Дает всего одно, но огромное преимущество. Так как в компонентах React нет локального состояния, то можно рассматривать UI как одну чистую функцию (термин из функционального программирования) которая получает одно состояние на вход в виде аргумента и возвращает пользовательский интерфейс (точнее, его React-представление), соответствующий этому состоянию. На практике такой подход дает возможность отладки в реальном времени, undo и redo, time travel. Здесь можно посмотреть, как это все сделано:
- debug.elm-lang.org/edit/Stamps.elm — дебаггер для Elm
- https://www.youtube.com/watch? v=Fo86aiBoomE — то же самое, но имплементировано в JS, используется дебагер из библиотеки cerebraljs
- www.youtube.com/watch? v=5yHFTN-_mOo — т о, что сделал парень из CircleCI, ребята используют ClojureScript Om, в котором также используется один объект для хранения состояния. Более детальное описание, как у них все это работает
Такой подход выводит тестирование и отладку веб приложений на принципиально новый уровень. Больше не нужны скриншоты и длинный список «шагов воспроизведения». Если вы встретили баг — просто скопируйте текущее состояние приложения и отдайте его разработчикам.
Иммутабельные структуры данных
В ряде случаев их использование позволяет написать более быстрый код. В котором, к тому же, будет меньше багов из-за отсутствия мутабельных объектов и значений. Несмотря на то, что Baobab не предлагает иммутабельных структур данных (только персистентные), он не рекомендует напрямую изменять дерево данных, а предлагает использовать для этого функции API.
Автор изображения (перед катом) — Stefpet, Сreative Commons 2.0.