Эта статья рассказывает о том, как создать проект с использованием БЭМ-технологий.
Мы шаг за шагом создадим страничку каталога товаров, пользуясь принципами БЭМ в CSS, возможностями писать декларативный JavaScript на фреймворке i-bem.js
и с использованием шаблонизатора BEMHTML
. Помогать делать всё это будут bem tools
, в частности — инструмент для разработки bem server
.
Важно: в статье нет особенных подробностей, её цель — получить проект максимально быстро. Текст, раскрывающий больше информации, пройдёт следующим постом.
Что такое БЭМ?
Для начала небольшое лирическое отступление для тех, кто не в курсе, что обозначает эта аббревиатура.
БЭМ расшифровывается как «Блок, Элемент, Модификатор». Это методология разработки web-проектов, способ удобно делить интерфейс на отдельные штучки, применимый для любой технологии. Кроме того, БЭМ — это набор инструментов для автоматизации работы. И наконец, БЭМ — это возможность создания интерфейсных библиотек для быстрой и эффективной разработки.Если ранее вы не сталкивались с БЭМ, вам стоит вначале просмотреть материалы сайта bem.info, а затем вернуться к этой статье.
Для тех, кому больше нравится видео, могу предложить запись доклада с WebConf Riga 2012 (на английском) или выступление Сергея Бережного (veged) на РИТ 2011.
Необходимые инструменты
Чтобы пройти по всем шагам этого мануала, вам нужно установить bem tools. Это набор инструментов с command line интерфейсом для оперирования БЭМ-сущностями и сборки проекта. Инструкция по установке есть в описании репозитория.
Создание собственного репозитория проекта
Проще всего создать свой проект простым копированием существующего репозитория с подходящей структорой. Для проекта с использованием полного стека БЭМ подойдёт репозиторий project-stub.
$ git clone git://github.com/bem/project-stub.git my-pretty-project
$ cd my-pretty-project/
$ rm -rf .git
$ git init
Затем проект нужно собрать. Для этого запускается команда
make
Это занимает некоторое время, потому что именно в этот момент в директорию проекта устанавливаются все необходимые npm-пакеты.
В конце вы увидите следующее сообщение:
info: Server is listening on port 8080. Point your browser to http://localhost:8080/
На вашем компьютере запустился bem server
— инструмент для разработки, который будет автоматически пересобирать ваш проект, если вы внесете в него изменения.
Внесение изменения в страницы
Сейчас на вашем проекте есть одна страница index.html, которую вы можете открыть в браузере.
Первый запрос к странице будет обрабатываться заметное время, потому что в этот момент bem server подгружает необходмые для её сборки библиотеки.
Структура проекта предполагает, что блоки будут размещены в директории desktop.blocks
, а страницы — в директории desktop.bundles
.
Вообще, строго говоря, desktop.bundles
хранит «набор» блоков. Это могут быть частоиспользуемые блоки нескольких страниц (то, что обычно называют common
), наборы, объединяющие несколько страниц (all
, если объединены все страницы) или — самый простой случай — наборы блоков, каждый из которых соответствует одной странице. Здесь будет рассматриваться последний, простой вариант.
Вы можете отредактировать страницу, меняя файл desktop.bundles/index/index.bemjson.js
.
Описание блока в bemjson
Сначала мы разместим на странице Шапку. В терминах БЭМ это блок head
:
{ block: 'head' }
Здесь и далее полный код страницы на разных стадиях можно будет находить на Gist: https://gist.github.com/4175550.
Перезагрузив страницу, вы увидите что в ней появился соответствующий <div>
.
Шапку мы наполним содержанием: форма поиска, логотип и раскладка, располагающая содержание как нужно.
Сначала в BEMJSON-описании страницы внутрь блока head
помещаем блок layout
с двумя элементами: left
и right
.
content: [
{
block: 'head',
content: {
block: 'layout',
content: [
{
elem: 'left',
content: 'left here'
},
{
elem: 'right',
content: 'right here'
}
]
}
}
]
https://gist.github.com/4175573
Это создаст необходимую разметку (вы можете увидеть её, обновив страницу), к которой нужно написать стили. То есть реализовать блок layout
в технологии CSS.
Создание блока
Для создания файла технологии воспользуемся командой bem create
.
$ bem create -l desktop.blocks/ -T css -b layout
Команда создаст файл desktop.blocks/layout/layout.css
, в котором уже есть селектор, соответствующий файлу блока. Правило нужно дополнить соответственно назначению блока.
Сейчас можно просто скопировать: https://gist.github.com/4175598
Использование блоков из библиотек
Вложенные в layout
блоки поисковой формы и логотипа реализовывать самостоятельно не нужно. Они уже реализованы в библиотеке bem-bl, достаточно просто задекларировать их на странице. То есть вставить BEMJSON-описание блока в страницу desktop.bundles/index/index.bemjson.js
Для нашей страницы мы воспользуемся блоками b-search и b-logo.
https://gist.github.com/4175640
Картинку для логотипа можно взять отсюда илиуказать свою.
Доопределение блоков библиотек
Доопределение в CSS
Используемый нами блок b-logo
предоставляет только нужную разметку. CSS для неё каждый разработчик может написать сам, потому что всем нужна разная разметка.Эту разметку мы поместим в блок b-logo
на своём уровне переопределения.
$ bem create -l desktop.blocks/ -T css -b b-logo
Разметку для блока можно взять отсюда: https://gist.github.com/4175675
То же самое для блока b-search
:
$ bem create block -l desktop.blocks/ -T css b-search
https://gist.github.com/4195433
Доопределение BEMHTML
Чтобы сделать страницу центрированной, нужен дополнительный контейнер. Для этого мы доопределим шаблоны блока b-page
, создав такой же блок на своём уровне. В качестве шаблонизатора используется BEMHTML3.
$ bem create -l desktop.blocks/ -b b-page -T bemhtml
В получившемся файле desktop.blocks/b-page/b-page.bemhtml
нужно написать код, оборачивающий контент блока в дополнительный контейнер.
block b-page, content: {
elem: 'body-i',
content: this.ctx.content
}
https://gist.github.com/4175742
Для получившейся разметки создаются свои CSS-правила:
$ bem create -l desktop.blocks/ -T css -b b-page
Контент для получившегося файла desktop.blocks/b-page/b-page.css
можно скопировать отсюда: https://gist.github.com/4175763
А для того, чтобы блок шапки был заметен на странице, я задам ему border:
$ bem create -l desktop.blocks/ -T css -b head
Контент для файла desktop.blocks/head/head.css
: https://gist.github.com/4175776.
BEMHTML шаблоны
BEMHTML шаблоны могут не просто определять теги, которым представлен блок, и их атрибуты, но и генерировать оформительский контент.
Например, давайте разместим на странице список товаров. Он представлен в BEMJSON-декларации страницы блоком goods
, и декларация содержит необходимые данные.
{
block: 'goods',
goods: [
{
title: 'Apple iPhone 4S 32Gb',
image: 'http://mdata.yandex.net/i?path=b1004232748_img_id8368283111385023010.jpg',
price: '259',
url: '/'
},
{
title: 'Samsung Galaxy Ace S5830',
image: 'http://mdata.yandex.net/i?path=b0206005907_img_id5777488190397681906.jpg',
price: '73',
url: '/'
},
...
}
https://gist.github.com/4176078
Для того, чтобы эти данные превратились в нужную разметку, блок должен быть реализован в технологии BEMHTML. Для внешнего вида — технология CSS. Поэтому можно создать блок во всех технологиях, предусмотренных по-умолчанию.
$ bem create -l desktop.blocks -b goods
В BEMHTML шаблоне блока desktop.blocks/goods/goods.bemhtml
нужно написать код, который превратит JSON с данными в элементы блока. А также, пользуясь модой tag
указать, какими DOM-элементами представить блок и его элементы.
block goods {
tag: 'ul'
...
elem item, tag: 'li'
elem title, tag: 'h3'
}
https://gist.github.com/4176118
Шаблон может создавать не только элементы блока, но и другие блоки. Например, цену товара можно завернуть в ссылку, используя для этого блок b-link
из библиотеки bem-bl
.
{
elem: 'price',
content: {
block: 'b-link',
url: item.url,
content: item.price
}
}
https://gist.github.com/4176996
Кроме того, для того, чтобы избежать каскада при оформлении этой ссылки стилями, её можно пометить как элемент блока goods
.
{
block: 'b-link',
mix: [{ block: 'goods', elem: 'link' }],
url: item.url,
content: item.price
}
https://gist.github.com/4177113
Также нужно пометить элементы о новых товарах модификатором и добавить выравнивающих элементов.
https://gist.github.com/4177157
CSS для блока можно скопировать отсюда https://gist.github.com/4177163.
Создавать блок отдельно в технологии CSS не нужно, потому что он изначально был создан со всеми необходимыми файлами.
Понадобится и CSS для IE. Он не входит в список технологий по умолчанию.
$ bem create block -l desktop.blocks/ -T ie.css goods
Содержание для получившегося файла desktop.blocks/goods/goods.ie.css
можно взять на Gist https://gist.github.com/4177174
Зависимости блоков
Помимо декларации нужно гарантировать подключение к странице шаблонов, CSS и JavaScript блока. Для этого у блока можно описать зависимости, это делается представлением блока в технологии deps.js
.
$ bem create -l desktop.blocks/ -T deps.js -b goods
Можно воспользоваться нестрогой зависимостью shouldDeps
, указав, что нужен блок b-link
.
({
shouldDeps: [
{ block: 'b-link' }
]
})
https://gist.github.com/4177031
Подключение библиотек
Хочется представить шапку и каждый товар модными прямоугольниками с тенью. Блок для этого я позаимствую из библотеки моего друга.
Там есть всего один блок, который называется box
и делает то, что мне нужно.
Чтобы получить код библиотеки, мне нужно указать её адрес в ./bem/make.js
, по аналогии с соседними библиотеками.
getLibraries: function() {
return {
'bem-bl': {
type: 'git',
url: 'git://github.com/bem/bem-bl.git',
treeish: '0.3'
},
'bemhtml' : {
type: 'git',
url: 'git://github.com/bem/bemhtml.git'
},
'john-lib' : {
type: 'git',
url: 'git://github.com/john-johnson/j.git'
}
};
}
https://gist.github.com/4177229
А также указать в настройках бандлов (страниц), что этот уровень нужно использовать при сборке. Это делается в файле desktop.bundles/.bem/level.js
.
exports.getConfig = function() {
return BEM.util.extend(this.__base() || {}, {
bundleBuildLevels: this.resolvePaths([
'../../bem-bl/blocks-common',
'../../bem-bl/blocks-desktop',
'../../bemhtml/common.blocks',
'../../john-lib/blocks/',
'../../desktop.blocks'
])
});
};
https://gist.github.com/4177250
К сожалению, пока что при изменении конфигурации проекта приходится перезапускать bem server
. Текущий процесс придётся прервать и снова набрать команду make
.
В будущих версиях необходимость перезапуска обещают убрать.
Миксы блоков и элементов
Теперь можно использовать блок box
Я могу просто обернуть им мои блоки. Но чтобы сэкономить разметку, можно смешать на одной DOM-ноде 2 блока. Это называется mix
.
Один из способов смешения — описать его во входных данных (BEMJSON).
В данном случае нужно смешать блок head
с блоком box
, изменив код страницы.
{
block: 'head',
mix: [ { block: 'box' } ],
content: ...
}
https://gist.github.com/4177292
Не забудьте записать блок box
в зависимости блока head
$ bem create -l desktop.blocks/ -T deps.js -b head
({
shouldDeps: [
{ block: 'box' }
]
})
https://gist.github.com/4235143
Смешивать можно не только блоки, но и элементы с блоками.
В шаблоне блока goods
смешаем каждый элемент item
с блоком box
.
content.push({
elem: 'item',
mods: mods,
mix: [{ block: 'box' }],
content: ...
https://gist.github.com/4177350
Декларативный JavaScript
Блоки с JavaScript функциональностью
Блок box
, который появился у меня на проекте благодаря подключенной сторонней библиотеке, предоставляет также и динамическую JavaScript-функциональность — он умеет сворачиваться.
Для сообщения, что я хочу использовать эту JavaScript-функциональность в шапке, мне нужно изменить описание блока head
, указав, что у примешиваемого блока box
есть JavaScript-реализацию:
mix: [{ block: 'box', js: true }]
https://gist.github.com/4202622
Также требуется разместить внутри блока элемент switcher
content: [
{
block: 'layout',
...
},
{
block: 'box',
elem: 'switcher'
}
]
https://gist.github.com/4202651
Получается блок со стрелочкой, которая умеет сворачивать и разворачивать его.
Доопределение JavaScript
Мне недостаточно JavaScript-функциональности блока box
. Я хочу, чтобы он сворачивался не только по вертикали, но и по горизонтали. При этом вносить изменения в чужую библиотеку я не могу. Но благодаря тому, что JavaScript блока написан с использованием декларативного фреймворка из блока i-bem, у меня есть возможность изменить (переопределить или доопределить) поведение блока на своём уровне.
bem create -l desktop.blocks -T js -b box
В получившемся файле desktop.blocks/box/box.js
нужно оставить только секцию onSetMod
, описывающую реакцию на установку модификаторов.
onSetMod : {
}
https://gist.github.com/4195865
В данном случае нужно реагировать на установку и снятие модификатора closed
:
onSetMod : {
'closed': {
'yes': function() {
// some functionality here
},
'': function() {
// some functionality here
}
}
}
https://gist.github.com/4195879
Создание новых страниц
Страницы — это тоже блоки, на своём уровне переопределения. Поэтому для их создания тоже можно воспользоваться командой bem create
:
bem create -l desktop.bundles -b contact
Флаг -T
можно не указывать, потому что bem create
благодаря настройкам уровня desktop.bundles
знает, что создаваемые на этом уровне блоки должны быть представлены в технологии BEMJSON. Так, появляется файл desktop.bundles/contact/contact.bemjson.js
с минимальным содержимым для страницы.
Новую страницу можно посмотреть по адресу http://localhost:8080/desktop.bundles/contact/contact.html
bem server
соберёт её HTML, JS и CSS файлы в момент первого обращения.
Production deployment
Всё время, пока мы разрабатывали проект, работал bem server
и пересобирал те части проекта, которые нуждаются в изменении при обновлении страниц.
Для выкатки в продакшен тоже нужна сборка проекта, но уже всего проекта целиком, вне зависимости от того, изменилось что-то или нет. Для этого можно воспользоваться командой bem make
.
Рекомендуется запускать локальную для данного проекта версию пакета:
./node_modules/bem/bin/bem make
creadits За подготовку разметки сайта большое спасибо tyv и gela-d.
Автор: toivonen