- PVSM.RU - https://www.pvsm.ru -

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

Введение

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

Подготовка

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

gem install rails

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

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

rails new some_cool_project

Далее добавим гем «bem» в файл Gemfile, который находится в корне только что созданного проекта. Также я рекомендую вам добавить гемы high_voltage [6] и slim-rails [7]. Первый позволит создавать для верстальщика типовые страницы без каких-либо особых знаний рельс и руби, второй — великолепный шаблонизатор, который может значительно ускорить вёрстку, а также минифицировать выходной 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 [8] через команду:

rails g bem:spring

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

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

spring stop

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

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

Вёрстка

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

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

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

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

bem help create

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

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

spring bem create -l shared -a application

Можно и без spring:

bem create -l shared -a application

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

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

Если вы заглянете в файл манифеста, то увидете, что уровен shared подключен в него строкой import [11] '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

Вы можете увидеть полученный результат по адресу localhost [12]: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, поэтому приходится оформлять такие инициализации в виде функций и затем вызывать при этих двух событиях.

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

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

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

= 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 [16]. Но я посчитал этот вариант весьма громоздким и непростым, поэтому не стал его рассматривать всерьёз.

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

Заключение

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

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

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

Ссылки

https://github.com/gkopylov/bem [2]
https://github.com/gkopylov/bem_with_rails_and_less_example_app [13]

P.S.

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

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

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

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

Ещё немного хотел написать почему по-дефолту я решил использовать scss. Дело в том, что в сообществе рельс sass препроцессор пользуется большей популярностью и даже по дефолту при создании нового приложения включается гем с этим препроцессором. А ещё там есть отличная фича, которая как раз подходит к подходит для БЭМ — это использование амперсанда [17]. Уже многие в БЭМ сообществе отходят от написания отдельного файла под модификатор и пишут модификатор прямо в файле с блоком, а при помощи этого сассного амперсанда можно как раз делать такие штуки прямо в стилях для блока.

Автор: gkopylov

Источник [18]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/ruby/64418

Ссылки в тексте:

[1] этого: http://habrahabr.ru/post/192972/

[2] гем: https://github.com/gkopylov/bem

[3] rvm: https://rvm.io/

[4] инструкции: https://rvm.io/rvm/install

[5] отсюда: http://railsinstaller.org/

[6] high_voltage: https://github.com/thoughtbot/high_voltage

[7] slim-rails: https://github.com/slim-template/slim-rails

[8] spring: https://github.com/rails/spring

[9] этой: http://habrahabr.ru/post/203440/

[10] тут: http://ru.bem.info/tools/bem/bem-tools/levels/

[11] import: http://habrahabr.ru/users/import/

[12] localhost: http://localhost

[13] на гитхабе: https://github.com/gkopylov/bem_with_rails_and_less_example_app

[14] bem-on-rails: https://github.com/verybigman/bem-on-rails

[15] патч: https://github.com/verybigman/bem-on-rails/pull/11

[16] half-pipe: https://github.com/d-i/half-pipe/releases

[17] амперсанда: http://jonsuh.com/blog/sass-bem-selector-and-trailing-ampersand/

[18] Источник: http://habrahabr.ru/post/229121/