- PVSM.RU - https://www.pvsm.ru -
Эта статья рассказывает о том, как создать проект с использованием БЭМ-технологий.
Мы шаг за шагом создадим страничку каталога товаров [1], пользуясь принципами БЭМ в CSS, возможностями писать декларативный JavaScript на фреймворке i-bem.js и с использованием шаблонизатора BEMHTML. Помогать делать всё это будут bem tools, в частности — инструмент для разработки bem server.

Важно: в статье нет особенных подробностей, её цель — получить проект максимально быстро. Текст, раскрывающий больше информации, пройдёт следующим постом.
Для начала небольшое лирическое отступление для тех, кто не в курсе, что обозначает эта аббревиатура.
БЭМ расшифровывается как «Блок, Элемент, Модификатор». Это методология разработки web-проектов, способ удобно делить интерфейс на отдельные штучки, применимый для любой технологии. Кроме того, БЭМ — это набор инструментов для автоматизации работы. И наконец, БЭМ — это возможность создания интерфейсных библиотек для быстрой и эффективной разработки.Если ранее вы не сталкивались с БЭМ, вам стоит вначале просмотреть материалы сайта bem.info [2], а затем вернуться к этой статье.
Для тех, кому больше нравится видео, могу предложить запись доклада с WebConf Riga 2012 [3] (на английском) или выступление Сергея Бережного ( [4]veged [5]) на РИТ 2011.
Чтобы пройти по всем шагам этого мануала, вам нужно установить bem tools [6]. Это набор инструментов с command line интерфейсом для оперирования БЭМ-сущностями и сборки проекта. Инструкция по установке есть в описании репозитория.
Проще всего создать свой проект простым копированием существующего репозитория с подходящей структорой. Для проекта с использованием полного стека БЭМ подойдёт репозиторий project-stub [7].
$ 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 [8], которую вы можете открыть в браузере.
Первый запрос к странице будет обрабатываться заметное время, потому что в этот момент bem server подгружает необходмые для её сборки библиотеки.
Структура проекта предполагает, что блоки будут размещены в директории desktop.blocks, а страницы — в директории desktop.bundles.
Вообще, строго говоря, desktop.bundles хранит «набор» блоков. Это могут быть частоиспользуемые блоки нескольких страниц (то, что обычно называют common), наборы, объединяющие несколько страниц (all, если объединены все страницы) или — самый простой случай — наборы блоков, каждый из которых соответствует одной странице. Здесь будет рассматриваться последний, простой вариант.
Вы можете отредактировать страницу, меняя файл desktop.bundles/index/index.bemjson.js.
Сначала мы разместим на странице Шапку. В терминах БЭМ это блок head:
{ block: 'head' }
Здесь и далее полный код страницы на разных стадиях можно будет находить на Gist: https://gist.github.com/4175550 [9].
Перезагрузив страницу, вы увидите что в ней появился соответствующий <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 [10]

Это создаст необходимую разметку (вы можете увидеть её, обновив страницу), к которой нужно написать стили. То есть реализовать блок layout в технологии CSS.
Для создания файла технологии воспользуемся командой bem create.
$ bem create -l desktop.blocks/ -T css -b layout
Команда создаст файл desktop.blocks/layout/layout.css, в котором уже есть селектор, соответствующий файлу блока. Правило нужно дополнить соответственно назначению блока.
Сейчас можно просто скопировать: https://gist.github.com/4175598 [11]
Вложенные в layout блоки поисковой формы и логотипа реализовывать самостоятельно не нужно. Они уже реализованы в библиотеке bem-bl [12], достаточно просто задекларировать их на странице. То есть вставить BEMJSON-описание блока в страницу desktop.bundles/index/index.bemjson.js
Для нашей страницы мы воспользуемся блоками b-search [13] и b-logo [14].
https://gist.github.com/4175640 [15]
Картинку для логотипа можно взять отсюда [16] илиуказать свою.

Используемый нами блок b-logo предоставляет только нужную разметку. CSS для неё каждый разработчик может написать сам, потому что всем нужна разная разметка.Эту разметку мы поместим в блок b-logo на своём уровне переопределения.
$ bem create -l desktop.blocks/ -T css -b b-logo
Разметку для блока можно взять отсюда: https://gist.github.com/4175675 [17]
То же самое для блока b-search:
$ bem create block -l desktop.blocks/ -T css b-search
https://gist.github.com/4195433 [18]

Чтобы сделать страницу центрированной, нужен дополнительный контейнер. Для этого мы доопределим шаблоны блока b-page, создав такой же блок на своём уровне. В качестве шаблонизатора используется BEMHTML3 [19].
$ 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 [20]

Для получившейся разметки создаются свои CSS-правила:
$ bem create -l desktop.blocks/ -T css -b b-page
Контент для получившегося файла desktop.blocks/b-page/b-page.css можно скопировать отсюда: https://gist.github.com/4175763 [21]
А для того, чтобы блок шапки был заметен на странице, я задам ему border:
$ bem create -l desktop.blocks/ -T css -b head
Контент для файла desktop.blocks/head/head.css: https://gist.github.com/4175776 [22].

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 [23]
Для того, чтобы эти данные превратились в нужную разметку, блок должен быть реализован в технологии 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 [24]

Шаблон может создавать не только элементы блока, но и другие блоки. Например, цену товара можно завернуть в ссылку, используя для этого блок b-link из библиотеки bem-bl.
{
elem: 'price',
content: {
block: 'b-link',
url: item.url,
content: item.price
}
}
https://gist.github.com/4176996 [25]
Кроме того, для того, чтобы избежать каскада при оформлении этой ссылки стилями, её можно пометить как элемент блока goods.
{
block: 'b-link',
mix: [{ block: 'goods', elem: 'link' }],
url: item.url,
content: item.price
}
https://gist.github.com/4177113 [26]

Также нужно пометить элементы о новых товарах модификатором и добавить выравнивающих элементов.
https://gist.github.com/4177157 [27]
CSS для блока можно скопировать отсюда https://gist.github.com/4177163 [28].
Создавать блок отдельно в технологии 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 [29]
Помимо декларации нужно гарантировать подключение к странице шаблонов, 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 [30]
Хочется представить шапку и каждый товар модными прямоугольниками с тенью. Блок для этого я позаимствую из библотеки моего друга [31].
Там есть всего один блок, который называется 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 [32]
А также указать в настройках бандлов (страниц), что этот уровень нужно использовать при сборке. Это делается в файле 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 [33]
К сожалению, пока что при изменении конфигурации проекта приходится перезапускать bem server. Текущий процесс придётся прервать и снова набрать команду make.
В будущих версиях необходимость перезапуска обещают убрать.
Теперь можно использовать блок box Я могу просто обернуть им мои блоки. Но чтобы сэкономить разметку, можно смешать на одной DOM-ноде 2 блока. Это называется mix.
Один из способов смешения — описать его во входных данных (BEMJSON).
В данном случае нужно смешать блок head с блоком box, изменив код страницы.
{
block: 'head',
mix: [ { block: 'box' } ],
content: ...
}
https://gist.github.com/4177292 [34]

Не забудьте записать блок box в зависимости блока head
$ bem create -l desktop.blocks/ -T deps.js -b head
({
shouldDeps: [
{ block: 'box' }
]
})
https://gist.github.com/4235143 [35]

Смешивать можно не только блоки, но и элементы с блоками.
В шаблоне блока goods смешаем каждый элемент item с блоком box.
content.push({
elem: 'item',
mods: mods,
mix: [{ block: 'box' }],
content: ...
https://gist.github.com/4177350 [36]


Блок box, который появился у меня на проекте благодаря подключенной сторонней библиотеке, предоставляет также и динамическую JavaScript-функциональность — он умеет сворачиваться.
Для сообщения, что я хочу использовать эту JavaScript-функциональность в шапке, мне нужно изменить описание блока head, указав, что у примешиваемого блока box есть JavaScript-реализацию:
mix: [{ block: 'box', js: true }]
https://gist.github.com/4202622 [37]
Также требуется разместить внутри блока элемент switcher
content: [
{
block: 'layout',
...
},
{
block: 'box',
elem: 'switcher'
}
]
https://gist.github.com/4202651 [38]
Получается блок со стрелочкой, которая умеет сворачивать и разворачивать его.

Мне недостаточно JavaScript-функциональности блока box. Я хочу, чтобы он сворачивался не только по вертикали, но и по горизонтали. При этом вносить изменения в чужую библиотеку я не могу. Но благодаря тому, что JavaScript блока написан с использованием декларативного фреймворка из блока i-bem [39], у меня есть возможность изменить (переопределить или доопределить) поведение блока на своём уровне.
bem create -l desktop.blocks -T js -b box
В получившемся файле desktop.blocks/box/box.js нужно оставить только секцию onSetMod, описывающую реакцию на установку модификаторов.
onSetMod : {
}
https://gist.github.com/4195865 [40]
В данном случае нужно реагировать на установку и снятие модификатора closed:
onSetMod : {
'closed': {
'yes': function() {
// some functionality here
},
'': function() {
// some functionality here
}
}
}
https://gist.github.com/4195879 [41]
Страницы — это тоже блоки, на своём уровне переопределения. Поэтому для их создания тоже можно воспользоваться командой 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 файлы в момент первого обращения.
Всё время, пока мы разрабатывали проект, работал bem server и пересобирал те части проекта, которые нуждаются в изменении при обновлении страниц.
Для выкатки в продакшен тоже нужна сборка проекта, но уже всего проекта целиком, вне зависимости от того, изменилось что-то или нет. Для этого можно воспользоваться командой bem make.
Рекомендуется запускать локальную для данного проекта версию пакета:
./node_modules/bem/bin/bem make
Автор: toivonen
Источник [44]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/22469
Ссылки в тексте:
[1] страничку каталога товаров: http://toivonen.github.com/online-shop-dummy/desktop.bundles/index/index.html
[2] bem.info: http://bem.info/
[3] запись доклада с WebConf Riga 2012: https://vimeo.com/53219242
[4] выступление Сергея Бережного (: http://my.comdi.com/event_export/_rec/rit/01/?room=2&rec=25
[5] veged: http://habrahabr.ru/users/veged/
[6] bem tools: https://github.com/bem/bem-tools
[7] project-stub: https://github.com/bem/project-stub
[8] index.html: http://localhost:8080/desktop.bundles/index/index.html
[9] https://gist.github.com/4175550: https://gist.github.com/4175550
[10] https://gist.github.com/4175573: https://gist.github.com/4175573
[11] https://gist.github.com/4175598: https://gist.github.com/4175598
[12] библиотеке bem-bl: http://bem.github.com/bem-bl/index.ru.html
[13] b-search: http://bem.github.com/bem-bl/sets/common-desktop/b-search/b-search.ru.html
[14] b-logo: http://bem.github.com/bem-bl/sets/common-desktop/b-logo/b-logo.ru.html
[15] https://gist.github.com/4175640: https://gist.github.com/4175640
[16] отсюда: http://toivonen.github.com/online-shop-dummy/desktop.blocks/b-logo/b-logo.png
[17] https://gist.github.com/4175675: https://gist.github.com/4175675
[18] https://gist.github.com/4195433: https://gist.github.com/4195433
[19] 3: #ref3
[20] https://gist.github.com/4175742: https://gist.github.com/4175742
[21] https://gist.github.com/4175763: https://gist.github.com/4175763
[22] https://gist.github.com/4175776: https://gist.github.com/4175776
[23] https://gist.github.com/4176078: https://gist.github.com/4176078
[24] https://gist.github.com/4176118: https://gist.github.com/4176118
[25] https://gist.github.com/4176996: https://gist.github.com/4176996
[26] https://gist.github.com/4177113: https://gist.github.com/4177113
[27] https://gist.github.com/4177157: https://gist.github.com/4177157
[28] https://gist.github.com/4177163: https://gist.github.com/4177163
[29] https://gist.github.com/4177174: https://gist.github.com/4177174
[30] https://gist.github.com/4177031: https://gist.github.com/4177031
[31] библотеки моего друга: https://github.com/john-johnson/j
[32] https://gist.github.com/4177229: https://gist.github.com/4177229
[33] https://gist.github.com/4177250: https://gist.github.com/4177250
[34] https://gist.github.com/4177292: https://gist.github.com/4177292
[35] https://gist.github.com/4235143: https://gist.github.com/4235143
[36] https://gist.github.com/4177350: https://gist.github.com/4177350
[37] https://gist.github.com/4202622: https://gist.github.com/4202622
[38] https://gist.github.com/4202651: https://gist.github.com/4202651
[39] декларативного фреймворка из блока i-bem: http://bem.github.com/bem-bl/sets/common-desktop/i-bem/i-bem.ru.html
[40] https://gist.github.com/4195865: https://gist.github.com/4195865
[41] https://gist.github.com/4195879: https://gist.github.com/4195879
[42] tyv: https://github.com/tyv
[43] gela-d: https://github.com/gela-d
[44] Источник: http://habrahabr.ru/post/162385/
Нажмите здесь для печати.