Введение
В этой статье я хотел бы рассказать о технике вёрстки по БЭМу в рельсовых проектах. Я ещё не видел подобных руководств(кроме, может быть, этого, но оно мало подходит в качестве руководства и о нём ещё расскажу дальше), поэтому решил написать эту статью. Кроме того, я создал гем, который упростит интеграцию БЭМ и рельс, о нём и как его использовать я тоже напишу дальше.
Подготовка
Для начала вам нужно установить собственно руби и рельсы. Я предпочитаю 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
Вы можете увидеть полученный результат по адресу 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 он компилирует отдельный файл
— возможность livereload injection(теоретически это сделать проще чем с импортом)
среди минусов:
— невозможность использования миксинов или переменных
Так что решение использовать ту или иную директиву нужно принимать обдуманно и в зависимости от проекта.
Ещё немного хотел написать почему по-дефолту я решил использовать scss. Дело в том, что в сообществе рельс sass препроцессор пользуется большей популярностью и даже по дефолту при создании нового приложения включается гем с этим препроцессором. А ещё там есть отличная фича, которая как раз подходит к подходит для БЭМ — это использование амперсанда. Уже многие в БЭМ сообществе отходят от написания отдельного файла под модификатор и пишут модификатор прямо в файле с блоком, а при помощи этого сассного амперсанда можно как раз делать такие штуки прямо в стилях для блока.
Автор: gkopylov