Rails 5 и Vue.js: как перестать мучаться с фронтендом и начать жить

Kak perestat muchatsya s frontendom i nachat zhit

Забавно. Времена меняются. То, от чего всегда отказывался, открещивался и думал, что ничто не заставит тебя этим заниматься, вдруг начинаешь делать, и делать с удовольствием.

Лично для меня концепция фронтенда долго была непонятной, а скрещивание с бекендом — магией. Появился Node — стало модно фигачать и бекенд на js. Появился Angular — разработчики начали прикручивать его ко всему подряд. Затем появился React, Flux/Redux, но вся эта фронтендовая жизнь проходила мимо меня. А всё потому, что как только я предпринимал очередную попытку освоить новый появляющийся мир, я тонул в болоте обвеса, инструментария, модных практик, новых способов организации файлов, нового всего. Новый фреймворк не успевал выйти, как он становился уже не модным или концептуально неправильным. Вообще никакой стабильности! И банально было жалко тратить время на изучение того, что может не пригодиться.

За что многие любят Rails? Да за Rails way! Есть множество способов сделать одну и ту же задачу, но тебе из всех способов предлагается именно проверенный временем и многими разработчиками вариант. Никто не заставит тебя сделать именно так, но если так сделаешь, то всё заработает из коробки! В мире JS, к сожалению, было не так. Ну или, по крайней мере, так было раньше.

По работе же приходилось работать над связкой Rails+Angular где, благодаря хорошей изначальной архитектуре, всё было благополучно с поддержкой и развитием проекта. Но проект протаскивался через rails assets pipeline и такое решение вызывало много вопросов у новых разработчиков.

На прошлой конференции Railsclub, после доклада Zach'a Briggs, мы еще часа полтора проболтали на тему, как они решают те или иные проблемы с фронтом, и что для всех это боль, но нынешнее время требует новых решений. Доклад призывал "Дайте JS еще один шанс" о Vue.js и Зак меня убедил, я решил дать JS еще один шанс.

Что такое Vue?

Vue.js — это фреймворк, который завоевал популярность, поскольку из коробки идет в Laravel (php-шном клоне рельсы). В Rails в какой-то момент стали использовать jQuery, а в истории Laravel в какой-то момент включили vue. Возможно потому он не особенно на слуху. Хотя выглядит так, что его популярность растёт день ото дня.

Из преимуществ можно выделить то, что при написании движка для рендеринга/ререндеринга страниц разработчиков консультировал и активно помогал автор движка для реакта. Поэтому Vue во многих случаях не только не уступает крутому react'у, но и даже превосходит по скорости/производительности.

Но важнее, и это и стало причиной, почему я решил дать ему шанс — он предлагает иттеративную интеграцию. Иттеративная интеграция — меняй свой фронт по чуть-чуть, шаг за шагом. Хочется чуть-чуть добавить интерактивности твоей страничке — просто используй одно Vue приложение, только в одном нужном тебе месте. Захотелось использовать компоненты — нет проблем, добавь здесь и здесь, не обязательно вкручивать SPA в каждый проект. Нужно много фронта в разных частях? — сделай отдельные микро-vue приложения, по одному на контроллер, в любом случае твой бекенд, а соответственно и контроллер отталкивается от ресурсов, которыми ты манипулируешь. А хочешь SPA — да пожалуйста, вот тебе и Vue-resource, который позволит общаться с API, вот и Vuex — для организации flux-архитектуры. Жги!

Иван Шаматов научит тебя всему, о чём говорится в этой статье Записаться

rails/webpacker

Незнаю как вы, но я очень жду релиза Rails 5.1, хотя бы по той причине, что нам обещается отличный инструмент для работы с фронтом. Гем webpacker решает за разработчика множество изначальных вопросов по вкручиванию фронта в rails приложение. Ту самую организацию файлов, дефолтных конфигов, пакетных менеджеров и остального, что приходилось в любом проекте делать руками.

Конечно, гем еще сыроват, но такого шага от rails-community давно не хватало. Да и опробовать его в деле можно уже сегодня. Так что довольно трёпа, погнали!

It's coding time!

У меня есть желание (и эй! не дай этому желанию угаснуть) — написать серию статей про vue + rails. А примером у нас будет приложение по бронированию билетов в кино. Но чтобы закрыть сегодняшний топик, а именно показать, как сделать простейший сетап для фронтенда, будет достаточно пустого приложения. Так давай его сделаем.

$ rails new cinematronix

Setup

Первым же делом добавим необходимые гемы: webpack — для совершения фронтовой магии, и foreman — для запуска нескольких процессов одновременно и про него будет чуть ниже.

# Gemfile
gem 'webpacker'
gem 'foreman'
$ bundle install

После того, как мы установили гемы, нам стали доступны следующие команды в rails.

$ bin/rails webpacker:install
$ bin/rails webpacker:install:vue
$ bin/yarn install

Первая команда делает общий сетап фронтенда. И знаешь что? Я даже не хочу рассказывать, что там происходит. Просто потому что это вообще не важно для того, чтобы начать. В голову лезут светлые воспоминания, когда только начинал работать с рельсой и делал проекты без какого-либо понимания, как и что работает.

Вторая — генерирует темплейт, настройки и вообще устанавливает vue.js. И всё это одной строкой.

А третья установит необходимые npm-пакеты, они прописаны в package.json в корне проекта.

Vue app

Выполнив сетап, у нас появится в директории app папка javascript. Ага, теперь frontend — это вам не ассет какой-нибудь. А прямо сущность первого порядка. Я немного поменял дефолтный код, но он очень похож на этот. По умолчанию, да ты и сам всё видишь, у тебя практически пустой application.js. А код похожий на тот, что представлен ниже, находится в hello_vue.js.

Дело в том, что webpacker нам предлагает создавать pack'и. Я уверен, что это очень удобно, когда у тебя несколько frontend-приложений в проекте. Но для сегодняшних целей будет додстаточно перетащить этот код в application.js и убрать всяческие упоминания "Hello".

// app/javascript/packs/application.js

import Vue from 'vue'
import App from '../components/app.vue'

document.addEventListener('DOMContentLoaded', () => {
  document.body.appendChild(document.createElement('app'))
  const app = new Vue({
    el: 'app',
    template: '<App/>',
    components: { App }
  })

  console.log(app)
})

Этот кусочек кода делает вот что: ждет, когда всё DOM-дерево загрузится, а после начинает инициализировать vue-приложение. Работает идентично jQuery.ready(), только без jQuery.

Что я еще изменил? — путь до app.vue. Vue — компонентный фреймворк, и в своих руководствах рекомендует убирать компоненты в подпапку components (чего и вам желаю).

Так уж вышло, что не обошел стороной и компонент App.vue. Но тут я всего-лишь добавил отступы внутри каждой из составляющих компонента. Это исключительно для удобства, чтобы в любимом Sublime можно было сворачивать каждый из тегов, чтоб не мешал работать.

// app/javascript/components/app.vue

<template>
  <div id='app'>
    <p>{{ message }}</p>
  </div>
</template>

<script>
  export default {
    data: function () {
      return {
        message: "Welcome to Cinematronix!"
      }
    }
  }
</script>

<style scoped>
  p {
    font-size: 2em;
    text-align: center;
  }
</style>

Вот так выглядит очень простой Vue-компонент. Он состоит из шаблона, кусочка логики и стилей, привязанных к конкретно этому шаблону. А вообще, у vue.js отличная документация и там это всё объясняется по-человечески, так что я предпочту оставить все эти базовые штуки на самостоятельное изучение.

Backend

Окей! Теперь нам нужно как-то это самое приложение отгружать пользователю. Поэтому в layout добавим javascript_pack_tag — это новый хелпер от webpacker'a, который берет указанный файл из папки app/javascript/packs в качестве точки входа и собирает приложение уже по относительным путям внутри entry-point.

# app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>Cinematronix</title>
    <%= csrf_meta_tags %>

    <%= stylesheet_link_tag 'application', media: 'all' %>
    <%= javascript_pack_tag 'application' %>
  </head>

  <body>
    <%= yield %>
  </body>
</html>

Но у нас даже дефолтного контроллера нет, чтобы этот самый layout отдать. Поэтому выполним пару крайне знакомых команд.

$ bin/rails g controller Landing index
# config/routes.rb
root to: 'landing#index'

Ну и последнее, что осталось нам сделать — это удалить всё из нашей вьюшки в app/views/landing/index.html.erb. Чистим под ноль!

3.. 2.. 1.. Поехали!

Осталось совсем немного. Я уже упоминал, что мы используем foreman для запуска нескольких процессов в одном терминале. Мы конечно могли бы запускать rails-сервер в одной вкладке, а сборщик фронтенда в соседней вкладке, но это же не так удобно! Ах да, webpacker идет в комплекте со специальным webpack-dev-сервером, который занимается тем, что компилирует приложение на лету и подгружает прямо (into your ear) в твой браузер.

# Procfile
backend: bin/rails s -p 3000
frontend: bin/webpack-dev-server

Есть лишь небольшой нюанс. Ассеты у нас грузятся с другого хоста, а точнее по умолчанию с localhost:8080, а значит нам нужно раскомментировать специальную настройку для development-окружения.

# config/environments/development.rb
Rails.application.configure do
  # Make javascript_pack_tag load assets from webpack-dev-server.
  config.x.webpacker[:dev_server_host] = 'http://localhost:8080'
  ...
end

Ну и последний штрих — всё это запускаем!

$ foreman start

А вот такой результат должен получиться в браузере:

Добро пожаловать

Итого

Что в сухом остатке? Хелло-ворлдное приложение на vue, прикрученное к рельсе всего за пару простых манипуляций. Без головной боли, без установок npm, модного yarn, без ручного написания package.json, изучения webpack.config'a, добавления трайнспайлеров, проставления их нужных версий, погружения в es5/es6 и так далее.

По сути, для начала разработки этого ничего знать не нужно. Надеюсь понятно, что я вовсе не за безграмотность. Я за то, чтобы входной порог должен быть ниже. И если тебе было трудно сделать первый шаг и начать работать с фронтендом, то вот он.

Источники


Хочешь быть таким же крутым, как Иван Шаматов?

Тогда записывайся на обучение! В своих статьях Иван делится лишь частью знаний. Научись большему со своим персональными наставником!

Назначить ментором Ivanshamatov