Мы в taaasty.ru уже полтора года живем на react-е и почти год без sprockets. Это так здорово, хочу поделиться. За одно расскажу как работает react с рельсами в режиме prerender.
ReactJS
Уже много сказано об этой технологии. Хочу лишь подтвердить, да, она прекрасна. Когда я пришел в проект он находился в стадии «давайте чинить эти баги, который вылезли из-з того что чинили вот тот». Во фронтенд желаний крутых фишек было тако много, что стало сразу понятно — мы не сможем учесть все возможные нюансы поведения в JS, а еще параллельно и править баги в легаси-коде без какого-то кардинального решения. И React полностью решает эту проблему.
Сначала мы подключили react-rails и делали компоненты в рельсовом окружении на coffee. Быстро выявились следующие проблемы:
1. JS код большой. Sprockets тормозной. Ожидания компиляции изменения становились все более и более утомительными.
2. Фронтендер-у приходилось настраивать ruby on rails, и не смотря на общее окружение в vagrant, это лишние действия, трата времени.
3. У фронтендер-а свой набор технологий — gulp, webpack и тп, свои пакеты, который в рельсы не подключишь.
4. Кроме фронтендера с html работает еще и верстальщик, а бывает и дизайнер. Им вообще не до рельс. А еще верстку периодически нужно показывать заказчику и это проще делать на статичных страницах по ftp, чем запускать ради этого рельсы.
В общем все шло к тому, чтобы фронтенд выделить в отдельный проект. Так и сделали. Весь React и JS перешел в отдельный репозиторий (его оставили открытым) — github.com/taaasty/web-static
Фронтендер и верстальщик работают со своим стеком технологий, никак не зависят от бакендера, делают свои быстрые сборочки. Могут запустить фронтенд-приложение и на травить его на продавшей или на тестовый сервер по-выбору.
Так как фронт-репозитоирий оставили открытым, подключили бесплатный travis для билдов, и сделали скрипт для выкладывания готового демо-билда на gh-pages — taaasty.github.io/web-static таким образом всегда можно посмотреть последнюю рабочую версию верстки и скриптов.
На этом этапе начали лучше понимать смысл поговорки «разделяй и властвуй».
Sprockets
Так как весь JS и CSS строятся в отдельном репозитории, в рельсах вообще пропала необходимость в сборке и sprockets (жуть). Статичные файлы подключаются в проект в виде git submodules:
$ cat .gitmodules
[submodule "vendor/static"]
path = vendor/static
url = https://github.com/taaasty/web-static.git
branch=develop
$ ag vendor/static config
config/application.rb
40: config.assets.paths << Rails.root.join('vendor/static/dist/stylesheets')
41: config.assets.paths << Rails.root.join('vendor/static/dist/scripts')
во фронт репозитирии лежат готовые файлы сборки для стилей и яваскрипта, их и запрашиваем прямо из html:
= javascript_include_tag 'desktop.production.js'
= stylesheet_link_tag 'desktop.production.css'
Теперь при развертывании нет прекомпиляции (assets:precompile не нужно), выкладывается все уже готовое. Длительность деплоя сократилась с 4-5 минут до 15 секунд.
А как хорошо стало бакендерам — вставил компонент, передал ему параметры нужной структуры и нет тебе никакого геморроя с html и JS. Все это уже решили на фронте. Фактически бакенд стал таким одним большим API — api.taaasty.com
react-rails
Каждый, кто узнает что у нас весь фронт на реакте, в первую очередь спрашивает — ну и как-же пререндер? Да отлично, работает с полпинка, все страницы отдаются с уже готовой версткой.
В ruby есть два рабочих движка для выполнения javascript (и пререндера компонентов). Это therubyracer (использует библиотеку libv8) и nodejs (реально запускает nodejs и отдает ему код на выполнение).
- therubyracer. Ест память. Течет она. Средняя скорость выполнения компонента ~10-20ms, но иногда на каждый 10-ти тысячный запрос) происходит подвисон до 100-200 мс.
- nodes безопасный способ выполнять JS код, НО, на каждое выполнение компонента (а их на странице может быть несколько) ruby запускает процесс nodejs (то есть загружает бинарный файл), скармливает исходный файл и забирает из STDOUT результаты. Средняя скорость выполнения компонента 50-80ms из которых большая часть уходит на подгрузку nodejs в память и его запуск.
Мы остановились на therubyracer. На данный момент он рендерит 2-3 тысячи компонентов в минуту. Больше года полет отличный. react-rails остался в проекте и выполняет только функции пререндера.
Комментарии (12)
ElianL
23.11.2015 20:41+1А зачем пытаться рендерить react через rails?
Мы уже сами пол года разрабатываем фронт как отдельный проект, по апи получаем все нужные данные, если нужен серверный рендеринг — запускаем сервер на ноде, который если нужно используем еще и как прокси для запросов к api(если все крутится на одном домене то можно этого и не делать, но для dev окружения необходимо)
Бекендеры рады — работают с чистыми данными, не парятся за html и прочие фронтедреские штуки
Мы(фронтендеры) тоже рады — от проекта к проекту работаем в привычном для себя окружении в независимости от того, что крутится на бекендеzorro1211
01.12.2015 06:43Не во всех компаниях такие крутые UI разработчики, которым в радость запускать node. Я ниже написал у меня в компании лютые закоренеллые .NET-овцы которые и так на меня люто смотрят за grunt/gulp/bower и npm. А из всех UI разработчиков я такой один проактивный. Так что для некоторых рендеринг react через rails/.net/java/php/… это вполне такой компромис.
zorro1211
01.12.2015 07:02А сколько у вас по времени занимает запрос? Спрашиваю потому что наткнулся вот на такую интересную библиотеку, которыя обещает 32 кратный прирост приизводительности в случае интеграции node & в данном случае .net — tjanczuk.github.io/edge/#/35. Хотя когда речь об ~1.5 мс vs. ~0 мс, то наверное не особо важно =)
zorro1211
01.12.2015 06:38У нас в компании все так же как и в статье, но на бекэнд .NET. Самый большой плюс это универсальный яваскрипт, все компоненты реюзабельны. Поверх функции которая рендерит копоненты на сервере еще сделали обертку чтобы в случае ошибки не вся страница ломалась, а только определенный компонент. Ещё один момент, наибольшое число проблем было с локализацией, например если бэкендшик добавил новый id для меню, который используется для локализованного текста а в локализации ещё этот текст не добавили, то у него выведется что-то типа `could not run Localization.menu[id][locale]`. Хотя если бы наши бекэнщики не были ленивыми, то писали бы юнит тесты вместо того чтобы жаловаться что теперь им нужно лезть в локализацию в чужой репозиторий и править там что-то, билдить, а то у них иначе страница ломается. Не понимают люди ещё что их ответственность заканчивается на добавлении id, а локализация и правильное отображение текста это уже забота UI.
dapi
01.12.2015 09:09Аналогично. Каждый компонент упакован в безопасный контейнер.
С локализацией аналогичная проблема. Есть два способа передавать локаль в компоненты — или передать сразу ВСЮ локаль и дальше компонент уже сам по ключам найдет переводы или передавать только конкретные тексты в конкретных параметрах. Применяем и такой и такой подход, в зависимости от массивности компонента.
xytop
Каким образом JSON отдаете? ActiveModel Serializers или что-то другое?
dapi
Для API grape (swagger) + презентеры через grape-entity.
Важный момент — сначала в названии презентеров исходили из названия модели, которую они презентуют (источника), потом стало понятно что именовать нужно согласно того компонента для которого эти данные готовятся. То есть одна и таже модель (публикация, например) по-разному представляется для разных компонентов.