[Перевод] Разработка React-приложений с использованием ReasonReact
Вы применяете React для создания пользовательских интерфейсов? Автор материала, перевод которого мы публикуем, говорит, что он тоже работает с React. Здесь он хочет рассказать о том, почему для написания React-приложений стоит использовать ReasonML.
React — это очень хороший инструмент для разработки интерфейсов. Можно ли сделать его ещё лучше? Для того чтобы улучшить работу с React, сначала надо понять его основные проблемы. В частности, проблему, у истоков которой лежит тот факт, что React — это JavaScript-библиотека.
React и JavaScript
Если присмотреться к React, то можно заметить, что некоторые из принципов, положенные в основу этой библиотеки, чужды для JavaScript. В частности, мы говорим об иммутабельности, о принципах функционального программирования и о системе типов.
Иммутабельность — это один из базовых принципов React. Мутации свойств компонентов или состояния приложения весьма нежелательны, так как подобное может привести к непредсказуемым последствиям. В JavaScript нет стандартных механизмов для обеспечения иммутабельности. Структуры данных делают иммутабельными либо придерживаясь неких соглашений, либо используя библиотеки наподобие immutable-js.
Библиотека React основана на принципах функционального программирования, так как React-приложения представляют собой композиции функций. Хотя в JavaScript имеются некоторые возможности функционального программирования, такие, как функции первого класса, функциональным языком программирования он не является. Если на JavaScript нужно писать хороший декларативный код, приходится прибегать к сторонним библиотекам вроде Lodash/fp или Ramda.
А что не так с системой типов? В React имеется концепция PropTypes. Её используют для имитации типов в JavaScript, так как этот язык, сам по себе, не является статически типизированным. Для того, чтобы пользоваться в JS выгодами статической типизации, опять же, приходится прибегать к сторонним инструментам, таким, как Flow и TypeScript.
Сравнение React и JavaScript
Как видите, JavaScript не совместим с базовыми принципами React.
Существует ли язык программирования, который лучше JavaScript согласуется с React?
На данный вопрос можно дать положительный ответ. Этот язык — ReasonML.
В Reason реализована иммутабельность. Так как он основан на OCaml, функциональном языке программирования, соответствующие возможности также оказываются встроенными в Reason. В этом языке, кроме того, присутствует собственная система типов, подходящая для React.
Сравнение React, JavaScript и Reason
Получается, что Reason совместим с базовыми принципами React.
Reason
Reason — это не новый язык. Он представляет собой альтернативный, напоминающий JavaScript, синтаксис и набор инструментов для OCaml — функционального языка программирования, который существует уже более 20 лет. Reason был создан разработчиками из Facebook, которые уже использовали OCaml в своих проектах (Flow, Infer).
OCaml
С-подобный синтаксис Reason делает OCaml доступным для программистов, которые знакомы с такими распространёнными языками, как JavaScript или Java. Reason даёт разработчику более качественную, в сравнении с OCaml, документацию, вокруг него сложилось постоянно растущее сообщество энтузиастов. Кроме того, то, что написано на Reason, несложно интегрировать с существующими JS-проектами.
Reason
Основой Reason является OCaml. Reason имеет ту же семантику, что и OCaml, различается лишь синтаксис. Это означает, что Reason даёт возможность писать OCaml-код, используя JavaScript-подобный синтаксис. В результате в распоряжении программиста оказываются такие замечательные возможности OCaml, как строгая система типов и механизм сопоставления с образцом (pattern matching).
Взглянем на фрагмент Reason-кода для того, чтобы ознакомиться с его синтаксисом.
let fizzbuzz = (i) =>
switch (i mod 3, i mod 5) {
| (0, 0) => "FizzBuzz"
| (0, _) => "Fizz"
| (_, 0) => "Buzz"
| _ => string_of_int(i)
};
for (i in 1 to 100) {
Js.log(fizzbuzz(i))
};
Хотя в этом фрагменте используется механизм сопоставления с образцом, он остаётся весьма похожим на JavaScript.
Единственным языком, который работает в браузерах, является JavaScript, поэтому, для того, чтобы писать для браузеров на каком бы то ни было языке, нам надо компилировать его в JavaScript.
BuckleScript
Одной из интереснейших возможностей Reason можно назвать компилятор BuckleScript, который берёт код, написанный на Reason и преобразует его в читабельный и производительный JS-код, кроме того, неплохо очищая его от неиспользуемых конструкций.
BuckleScript
Читабельность результатов работы BuckleScript придётся кстати в том случае, если вы работаете в команде, в которой не все знакомы с Reason. Эти люди, по крайней мере, смогут читать результирующий JS-код.
Код на Reason иногда так похож на JS-код, что компилятору вовсе не нужно его преобразовывать. Благодаря такому положению дел можно наслаждаться благами статической типизации Reason и писать код, выглядящий так, будто он написан на JavaScript.
Вот пример кода, который будет работать и в Reason и в JavaScript:
let add = (a, b) => a + b;
add(6, 9);
BuckleScript поставляется с четырьмя библиотеками. Это — стандартная библиотека, называемая Belt (стандартной библиотеки OCaml тут недостаточно), и привязки для JavaScript, Node.js и для API DOM.
Так как BuckleScript основан на компиляторе OCaml, компиляция оказывается очень быстрой — гораздо быстрее чем у Babel и в несколько раз быстрее чем у TypeScript.
Скомпилируем с помощью BuckleScript вышеприведённый фрагмент Reason-кода, содержащий функцию fizzbuzz()
, в JavaScript.
Компиляция Reason-кода в JavaScript с помощью BuckleScript
Как видите, JS-код оказался вполне читаемым. Выглядит он так, как будто написан человеком.
Программы, написанные на Reason, компилируются не только в JavaScript, но и в нативный код, и в байт-код. В результате, например, можно написать приложение на Reason и запустить его в браузере, на MacOS, на смартфонах, работающих под управлением Android и iOS. Существует игра Gravitron, написанная Джаредом Форсайтом на Reason. Её можно запускать на всех вышеупомянутых платформах.
Организация взаимодействия с JavaScript
BuckleScript даёт возможность организации взаимодействия Reason и JavaScript. Это означает не только возможность использования рабочего JS-кода в кодовой базе Reason, но и возможность взаимодействия кода, написанного на Reason, с этим JavaScript-кодом. Как результате, код, написанный на Reason, легко поддаётся интеграции в существующие JS-проекты. Более того, в Reason-коде можно использовать JavaScript-пакеты из NPM. Например, можно создать проект, в котором совместно используются Flow, TypeScript и Reason.
Однако всё не так уж и просто. Для того чтобы использовать JavaScript-код или библиотеки в Reason, их сначала надо портировать с использованием привязок (биндингов) Reason. Другими словами, нам, для того, чтобы воспользоваться строгой системой типов Reason, нужны типы для обычного JavaScript-кода.
Если вам нужно воспользоваться какой-нибудь JavaScript-библиотекой в Reason-коде, сначала стоит обратиться к Reason Package Index (Redex) и узнать, была ли эта библиотека уже портирована в Reason. Проект Redex представляет собой каталог библиотек и инструментов, написанных на Reason и JavaScript-библиотек с Reason-привязками. Если вам удалось найти в этом каталоге нужную библиотеку, её можно установить как зависимость и использовать в Reason-приложении.
Если же найти нужную библиотеку не удалось, вам придётся самостоятельно написать биндинги. Если вы только начинаете знакомство с Reason, учтите, что писать биндинги — это задача не для новичка. Это — одна из самых сложных задач, из тех, что приходится решать тем, кто программирует на Reason. На самом деле, это — тема для отдельной статьи.
Если вам нужен лишь некий ограниченный функционал какой-нибудь JavaScript-библиотеки, вам не нужно писать привязки для всей такой библиотеки. Сделать это можно только для необходимых функций или компонентов.
ReasonReact
В начале материала мы говорили о том, что он посвящён разработке React-приложений с использованием Reason. Заниматься этим можно благодаря библиотеке ReasonReact.
Возможно, сейчас вы думаете: «Мне всё ещё непонятно — почему надо писать React-приложения на Reason». Однако мы уже обсудили основную причину использования связки React и Reason, которая заключается в том, что React лучше совместим с Reason чем с JavaScript. Почему это так? Всё дело в том, что React был создан в расчёте на Reason, или, точнее, в расчёте на OCaml.
Путь к ReasonReact
Первый прототип React был разработан Facebook и был написан на Standard Meta Language (StandardML), на языке, который является родственником OCaml. Затем React перевели на OCaml, кроме того, React перенесли на JavaScript. Сделано это было из-за того, что весь веб использовал JavaScript и, вероятно, неразумным было бы делать заявления вроде: «А теперь мы будем писать UI на OCaml». Перевод React на JavaScript себя оправдал и привёл к широкому распространению этой библиотеки.
Как результат, все привыкли воспринимать React в виде JS-библиотеки. React, а также другие библиотеки и языки, такие как Elm, Redux, Recompose, Ramda, и PureScript, способствовали популяризации функционального стиля программирования в JavaScript. А благодаря распространению Flow и TypeScript в JavaScript стала популярна и статическая типизация. В итоге парадигма функционального программирования с использованием статических типов стала главенствующей в мире разработки фронтенда.
В 2006 году компания Bloomberg создала и перевела в разряд опенсорсных проектов компилятор BuckleScript, который преобразует OCaml в JavaScript. Это позволило им писать более качественный и безопасный фронтенд-код, используя строгую систему типов OCaml. Они взяли оптимизированный и очень быстрый компилятор OCaml и заставили его генерировать код на JavaScript.
Популярность функционального программирования и выпуск BuckleScript создали идеальный климат, который позволил Facebook вернуться к исходной идее React — библиотеки, которая изначальна была написана на StandardML.
ReasonReact
Они смешали семантику OCaml с синтаксисом JavaScript и создали Reason. Кроме того, они создали Reason-обёртку для React, представленную в виде библиотеки ReasonReact, которая обладает дополнительными функциями, такими, как инкапсуляция принципов Redux в компонентах с состоянием. Сделав это, они вернули React к его истокам.
О возможностях React в Reason
Когда библиотеку React переводили на JavaScript, возможности языка подгоняли под нужды React за счёт создания различных библиотек и инструментов. Подобный подход, в частности, означает необходимость в большом числе зависимостей для проектов. Уж не будем говорить о том, что подобные библиотеки постоянно развиваются, и в них регулярно происходят изменения, делающие их новые версии несовместимыми со старыми. Как результат, разработчику приходится очень серьёзно и осторожно относиться к обслуживанию библиотек, от которых зависят его проекты.
Это добавляет дополнительный уровень сложности в JavaScript-разработку. Например, типичное React-приложение обычно содержит, как минимум, зависимости, которые можно видеть на следующем рисунке.
Зависимости типичного React-приложения
Вот какие задачи решают эти зависимости:
- Статическая типизация — Flow/TypeScript.
- Иммутабельность — ImmutableJS.
- Маршрутизация — ReactRouter.
- Форматирование кода — Prettier.
- Линтинг — ESLint.
- Вспомогательные функции — Ramda/Lodash.
Теперь воспользуемся, вместо React для JavaScript, библиотекой ReasonReact. Нужны ли нам, при таком подходе, все эти зависимости?
Переход на ReasonReact
Проанализировав тот же список задач, которые раньше решались с помощью дополнительных средств, мы выясним, что все их можно решить с помощью встроенных средств ReasonReact. Подробности о них вы можете почитать здесь.
В приложении, подготовленном средствами ReasonReact, все эти и многие другие зависимости не нужны. Дело в том, что множество важнейших возможностей, которые упрощают разработку, уже включены в язык. Как результат — упрощается работа с зависимостями, и, по мере роста и развития приложения, эта работа не усложняется.
Всё это оказывается возможным благодаря использованию языка OCaml, которому уже более 20 лет. Это зрелый язык, базовые принципы и механизмы которого проверены временем и стабильны.
Что дальше?
Если вы родом из мира JavaScript, вам будет несложно начать работу с Reason благодаря тому, что синтаксис этого языка похож на JavaScript. Если вы раньше писали React-приложения, перейти на Reason вам будет ещё легче, так как вы можете, при работе с ReasonReact использовать все свои познания в области React. В основе ReasonReact лежит та же модель мышления, что и в основе React, процесс работы с ними так же очень схож. Это означает, что при переходе на Reason вам не придётся начинать с нуля. Вы разберётесь с Reason в процессе работы.
Лучший способ начать использовать Reason в своих проектах заключается в том, чтобы постепенно вводить в них фрагменты, написанные на Reason. Как уже было сказано, Reason-код можно использовать в JS-проектах, равно как и JS-код в Reason-проектах. Этот подход применим и при использовании ReasonReact. Можно взять ReasonReact-компонент и использовать его в традиционном React-приложении, написанном на JavaScript.
Именно такой вот инкрементальный подход был выбран разработчиками Facebook, которые широко использовали Reason при разработке мессенджера Facebook.
Если вы хотите написать React-приложение с использованием Reason и на практике изучить основы этого языка, взгляните на этот материал, где пошагово разбирается разработка игры «Крестики-нолики».
Итоги
У создателей Reason было два варианта действий. Первый заключался в том, чтобы взять JavaScript и как-то его улучшить. Если бы они избрали этот путь — им пришлось бы иметь дело с историческими недостатками JS.
Они, однако, избрали второй путь, связанный с OCaml. Они взяли OCaml — зрелый язык, обладающий отличной производительностью, и модифицировали его так, чтобы он стал похожим на JavaScript.
React тоже основан на принципах OCaml. Именно поэтому писать React-приложения гораздо легче и приятнее с использованием Reason. Работа с React в Reason предлагает более стабильный и безопасный подход к созданию React-компонентов, так как строгая система типов страхует разработчика и ему не приходится сталкиваться с большинством исторических проблем JavaScript.
Уважаемые читатели! А вы пробовали ReasonReact?