Как стать автором
Обновить

Вёрстка по БЭМу в Ruby on Rails

Время на прочтение 7 мин
Количество просмотров 12K

Введение


В этой статье я хотел бы рассказать о технике вёрстки по БЭМу в рельсовых проектах. Я ещё не видел подобных руководств(кроме, может быть, этого, но оно мало подходит в качестве руководства и о нём ещё расскажу дальше), поэтому решил написать эту статью. Кроме того, я создал гем, который упростит интеграцию БЭМ и рельс, о нём и как его использовать я тоже напишу дальше.

Подготовка


Для начала вам нужно установить собственно руби и рельсы. Я предпочитаю rvm. Следуйте инструкции по установке rvm, а затем установите рельсы через команду:

gem install rails


Ну или можете установить рельсы отсюда, если у вас Windows.

Затем создаём новый проект через команду:

rails new some_cool_project


Далее добавим гем «bem» в файл Gemfile, который находится в корне только что созданного проекта. Также я рекомендую вам добавить гемы high_voltage и slim-rails. Первый позволит создавать для верстальщика типовые страницы без каких-либо особых знаний рельс и руби, второй — великолепный шаблонизатор, который может значительно ускорить вёрстку, а также минифицировать выходной html.

Теперь выполним установку необходимых файлов для гема bem при помощи команды:

rails g bem:install


Установится конфиг config/initializers/bem.rb, в котором вы можете менять технологии(изначально это scss и js) и шаблоны для генерации файлов для каждой технологии lib/bem/templates/scss.tt и lib/bem/templates/js.tt. Допустим, мы хотим использовать less в нашем проекте, тогда эту технологию нужно прописать в конфиг config/initializers/bem.rb вместо scss:

BEM.configure do |config|
  config.technologies = [
    { :group => 'stylesheets', :extension => '.less', :name => 'less',
      :css_directive => '@import', :css_prefix => "'", :css_postfix => "';" },
    { :group => 'javascripts', :extension => '.js', :name => 'js' }
  ]
end


Также я очень рекомендую установить конфиг для spring через команду:

rails g bem:spring


Эта библиотека позволит подгружать среду в фоне, что позволит быстро генерировать блоки.

Если вы установили конфиг для spring, то выполните следующую команду:

spring stop


Это команда нужна для последующей перезагрузки spring.

Теперь можно уже приступать к вёрстке.

Вёрстка


В качестве примера для вёрстки, я выбрал вёрстку из этой статьи.

Блоки, уровни и манифесты создаются с помощью команды bem create. Это команда принимает на вход следующие флаги(значение флагов нужно указывать через пробел после них самих):

  • -b создаёт или использует блок.
  • -e создаёт или использует элемент.
  • -m создаёт модификатор.
  • -v значение модификатора.
  • -l создаёт или использует уровень.
  • -a создаёт или использует манифест.
  • -js флаг для создания файлов javascripts технологий.
  • -css флаг для создания файлов stylesheets технологий.


Вы также можете прочитать информацию о флагах и их использовании выполнив команду:

bem help create


Команда create будет создавать такуе же структуру файлов(как и тут).

Далее создадим наш будущий уровень, в котором будут подключаться наши блоки и манифест через команду:

spring bem create -l shared -a application


Можно и без spring:

bem create -l shared -a application


Но, как я говорил, spring будет держать среду в бэкграунде, что ускорит выполнение последующих команд.

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

Если вы заглянете в файл манифеста, то увидете, что уровень shared подключен в него строкой
@import 'shared/shared';

Действует простое правило — в манифестах подключаются уровни, в уровнях подключаются блоки.

Удаляем прошлый манифест app/assets/application.css, так как мы будем использовать теперь манифест app/assets/application.less

Далее создадим нужные нам блоки|элементы|модификаторы через подобные команды:

spring bem create -l shared -b clear --no-js
spring bem create -l shared -b page --no-js
spring bem create -l shared -b page -e head-line --no-js
spring bem create -l shared -b page -e line --no-js
spring bem create -l shared -b link -m menu -v active --no-js
...


Как видно, я указываю уровень shared, в котором будут находится блоки и ещё флаг --no-js, так как для этих блоков не будут использоваться js файлы.

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

Если вы ошиблись и написали не тот класс или включили блок не в тот уровень/манифест, всегда можно откатиться выполнив обратную команду удаления блока|элемента|модификатора|уровня через команду bem destroy. Она принимает все те же флаги, кроме --js и --css.

Далее, когда мы создадим нужные нам блоки|элементы|модификаторы, переходим к вёрстке html. Если вы установили гем high_voltage, о котором я уже писал, то достаточно будет просто создать папку:

mkdir app/views/pages


В которой будут находится нужные нам вьюхи. Если вы прочитали документацию к этому гему, то вам наверняка понравилась простота подключения и отображения вьюх. Например, вам нужна вёрстка для страницы «welcome». Создаём вьюху app/views/pages/welcome.html.slim и прописываем там нужный нам html. После запуска вебсервера через команду

rails s


Вы можете увидеть полученный результат по адресу http://localhost:3000/pages/welcome

Складываем все картинки в папку app/assets/images и все теги заменяем на рельсовые хэлперы image_tag. Адреса картинок в less файлах заменяем на image-url (хэлпер, который предоставляет гем less-rails).

К сожалению, в приведённом примере не используется js. Тем не менее шаблоны, которые предоставляются для него, позволяют легко написать нужную функцию и запустить её:

function your_function_name_initializer() {
// some code
}

$(function() {
  your_function_name_initializer();
});

$(window).bind('page:load', function() {
  your_function_name_initializer();
})


Почему так? Дело в том, что начиная с 4 версии рельс, используется гем turbolinks, который значительно ускоряет отображение страниц, но добавляет некоторые особенности в инициализации js, поэтому приходится оформлять такие инициализации в виде функций и затем вызывать при этих двух событиях.

Конечный вариант перенесённой вёрстки и самого рельсового проекта можно посмотреть на гитхабе.

Альтернативы


Есть также гем bem-on-rails, о статье про который я говорил в начале. Я уже было хотел использовать его, но у него есть множество минусов на данный момент, которые вынудили меня написать свой гем. Среди минусов — невозможность нормально работать с уровнями и манифестами(мне так и не удалось создать два манифеста со своими уровнями и раскидывать блоки по ним), плохочитаемый код(где-то 4 пробела в качестве таба, где-то два) и вообще невысокое качество кода(нет ни одного теста, некрасивая архитектура, такое чувство, что гем делался совсем впопыхах и туда просто накидан код и вообще мне не удалось его запустить на рельсах версии 4.1 — пришлось отправить патч), плохая документация, ну а также, если использовать вьюшные хэлперы этого гема, то может получится такое например(простой футер):

= b 'footer', content: [{ elem: 'el', elemMods: ['left'], content: ['ОАО «фирма»'] },
   { elem: 'el', elemMods: ['center'], content: [{ elem: 'nav-link', tag: 'a', content: ['ссылка'] },
   { elem: 'nav-link', tag: 'a', content: ['ещё сслыка']},
   { elem: 'nav-link', tag: 'a', content: ['и ещё ссылка'] }] },
   { elem: 'el', elemMods: ['right'], 
   content: ['<a href="http://ya.ru/">такие дела</a>'.html_safe] }]


Что явно не идёт на пользу читаемости и может запутать верстальщика.

Тем не менее, я считаю, что в целом это неплохая попытка подстраивания bem-tools под рельсы.

Есть ещё некоторые экзотические варианты встраивания ноды в рельсы, чтобы использовать нативно bem-tools через grunt, например half-pipe. Но я посчитал этот вариант весьма громоздким и непростым, поэтому не стал его рассматривать всерьёз.

И ещё, можно, конечно, использовать сами bem-tools в каком-нибудь отдельном репозитории, но процесс вёрстки и переноса при этом сильно усложнится — нужно будет писать какой-нибудь (баш)скрипт для автоматического переноса и замены урлов например, постоянно гоняя вёрстку туда и сюда. Ну это ещё ладно. Но хуже всего при этом происходит переключение с ветки на ветку в этих двух репозиториях — нужно перед запуском скрипта переноса убедиться, что мы используем правильную ветку в репозитории с рельсами и в репозитории с вёрсткой, а затем ещё и следить, когда какая ветка смержится и мержить её в другом репозитории тоже. Поэтому решение не самое хорошее.

Заключение


Итак, верстальщику достаточно будет просто клепать вьюхи и блоки с css|js не обязательно зная, что там творится внутри. А бекэнд разработчик вставит нужную логику. То есть процесс вёрстки сильно упрощается.

Библиотека(гем) bem, которую я написал, полностью открытая и использует самую демократичную лицензию MIT. Библиотеку я написал совсем недавно и буду рад вашим отзывам и предложениям, а также пуллреквестам на гитхаб.

Я довольно кратко обо всём написал, по каждому пункту можно написать ещё множество пояснений и рассуждений, поэтому если у вас возникнут вопросы, я с удовольствием отвечу на них в комментариях.

Ссылки


https://github.com/gkopylov/bem
https://github.com/gkopylov/bem_with_rails_and_less_example_app

P.S.

На момент написания статьи использовался гем bem версии 1.0.0. На данный момент версия этого гема 1.1.0 — в ней добавлена возможность подключения блоков|элементов|модификаторов прямо в манифест. Сделано это для того, чтобы верстальщику можно было самому менять порядок подключения стилей прямо в манифесте, вместо того, чтобы подключать туда уровни.

Чтобы воспользоваться данной опцией, достаточно добавить флаг -i в команду генерации. Таким образом созданные стили подключатся сразу в манифест без создания стилей для уровня.

Ещё немного хотел написать насчёт использования директив import и require. У обеих директив есть свои плюсы и минусы. Среди плюсов у import:
— можно подключать миксины и переменные
— всё сливается в один файл и быстрее отдаётся браузеру, вместо множество файлов, который генерирует require (но это и минус, см. ниже)
среди минусов:
— трудно отлаживать css — если возникнет ошибка препроцессора, будет трудно найти место этой ошибки
— не обновляются стили при изменении вложенных css файлов (для того, чтобы обновились стили нужно выполнить команду rm -rf tmp/assets/* и перезапустить рельсы)
Среди плюсов require:
— проще отлаживать, так как на каждый require он компилирует отдельный файл(в development среде)
— возможность livereload injection(теоретически это сделать проще чем с импортом)
среди минусов:
— невозможность использования миксинов или переменных
— может долго отдаваться скомпилированные файлы стилей браузеру, если директив 'require' много

Так что решение использовать ту или иную директиву нужно принимать обдуманно и в зависимости от проекта.

Ещё немного хотел написать почему по-дефолту я решил использовать scss. Дело в том, что в сообществе рельс sass препроцессор пользуется большей популярностью и даже по умолчанию при создании нового приложения включается гем с этим препроцессором. А ещё там(как впрочем, верно подметил faost и в less http://lesscss.org/features/#parent-selectors-feature) есть отличная фича, которая как раз подходит для БЭМ — это использование амперсанда. Уже многие в БЭМ сообществе отходят от написания отдельного файла под модификатор(если он небольшой) и пишут модификатор прямо в файле с блоком, а при помощи этого сассного амперсанда можно как раз делать такие штуки прямо в стилях для блока.
Теги:
Хабы:
+10
Комментарии 9
Комментарии Комментарии 9

Публикации

Истории

Работа

Ruby on Rails
17 вакансий
Программист Ruby
15 вакансий

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн
PG Bootcamp 2024
Дата 16 апреля
Время 09:30 – 21:00
Место
Минск Онлайн
EvaConf 2024
Дата 16 апреля
Время 11:00 – 16:00
Место
Москва Онлайн