Привет!

Мы в taaasty.ru уже полтора года живем на react-е и почти год без sprockets. Это так здорово, хочу поделиться. За одно расскажу как работает react с рельсами в режиме prerender.

image

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)


  1. xytop
    23.11.2015 16:09

    Каким образом JSON отдаете? ActiveModel Serializers или что-то другое?


    1. dapi
      23.11.2015 18:20

      Для API grape (swagger) + презентеры через grape-entity.

      Важный момент — сначала в названии презентеров исходили из названия модели, которую они презентуют (источника), потом стало понятно что именовать нужно согласно того компонента для которого эти данные готовятся. То есть одна и таже модель (публикация, например) по-разному представляется для разных компонентов.


  1. evnuh
    23.11.2015 17:52
    +4

    React на Node.JS, который собирается через Ruby on Rails. Просто мечта хипстера.


    1. xytop
      23.11.2015 18:00

      у нас в проекте для этого WebPack крутится, рельсы только сервят бандлы.


  1. ElianL
    23.11.2015 20:41
    +1

    А зачем пытаться рендерить react через rails?

    Мы уже сами пол года разрабатываем фронт как отдельный проект, по апи получаем все нужные данные, если нужен серверный рендеринг — запускаем сервер на ноде, который если нужно используем еще и как прокси для запросов к api(если все крутится на одном домене то можно этого и не делать, но для dev  окружения необходимо)

    Бекендеры рады — работают с чистыми данными, не парятся за  html и прочие фронтедреские штуки
    Мы(фронтендеры) тоже рады — от проекта к проекту работаем в привычном для себя окружении в независимости от того, что крутится на бекенде


    1. zorro1211
      01.12.2015 06:43

      Не во всех компаниях такие крутые UI разработчики, которым в радость запускать node. Я ниже написал у меня в компании лютые закоренеллые .NET-овцы которые и так на меня люто смотрят за grunt/gulp/bower и npm. А из всех UI разработчиков я такой один проактивный. Так что для некоторых рендеринг react через rails/.net/java/php/… это вполне такой компромис.


    1. zorro1211
      01.12.2015 07:02

      А сколько у вас по времени занимает запрос? Спрашиваю потому что наткнулся вот на такую интересную библиотеку, которыя обещает 32 кратный прирост приизводительности в случае интеграции node & в данном случае .net — tjanczuk.github.io/edge/#/35. Хотя когда речь об ~1.5 мс vs. ~0 мс, то наверное не особо важно =)


      1. dapi
        01.12.2015 09:08

        О, я это как раз искал, будем экспериментировать, спасибо.


  1. zorro1211
    01.12.2015 06:38

    У нас в компании все так же как и в статье, но на бекэнд .NET. Самый большой плюс это универсальный яваскрипт, все компоненты реюзабельны. Поверх функции которая рендерит копоненты на сервере еще сделали обертку чтобы в случае ошибки не вся страница ломалась, а только определенный компонент. Ещё один момент, наибольшое число проблем было с локализацией, например если бэкендшик добавил новый id для меню, который используется для локализованного текста а в локализации ещё этот текст не добавили, то у него выведется что-то типа `could not run Localization.menu[id][locale]`. Хотя если бы наши бекэнщики не были ленивыми, то писали бы юнит тесты вместо того чтобы жаловаться что теперь им нужно лезть в локализацию в чужой репозиторий и править там что-то, билдить, а то у них иначе страница ломается. Не понимают люди ещё что их ответственность заканчивается на добавлении id, а локализация и правильное отображение текста это уже забота UI.


    1. dapi
      01.12.2015 09:09

      Аналогично. Каждый компонент упакован в безопасный контейнер.

      С локализацией аналогичная проблема. Есть два способа передавать локаль в компоненты — или передать сразу ВСЮ локаль и дальше компонент уже сам по ключам найдет переводы или передавать только конкретные тексты в конкретных параметрах. Применяем и такой и такой подход, в зависимости от массивности компонента.


  1. zorro1211
    01.12.2015 06:45

    Скажите, а для рендеринга через rails, нужно ли делать экспорт компонентов в глобальный namespace? Если да, то какие инструмены для этого используете?


    1. dapi
      01.12.2015 09:10

      Нет, не нужно. Подключается скомпилированные на gulp-е js-файл со всеми компонентами.