Попробуй БЭМ на вкус!

в 14:18, , рубрики: css, html, javascript, templates, БЭМ, Веб-разработка, шаблонизатор, шаблоны, метки: , , , , , , ,

Эта статья рассказывает о том, как создать проект с использованием БЭМ-технологий.
Мы шаг за шагом создадим страничку каталога товаров, пользуясь принципами БЭМ в 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>.

HTML нового блока

Шапку мы наполним содержанием: форма поиска, логотип и раскладка, располагающая содержание как нужно.

Сначала в 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

Блок head с layout

Это создаст необходимую разметку (вы можете увидеть её, обновив страницу), к которой нужно написать стили. То есть реализовать блок 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

Использование блока b-link

Также нужно пометить элементы о новых товарах модификатором и добавить выравнивающих элементов.
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

Шапка с блоком box

Смешивать можно не только блоки, но и элементы с блоками.
В шаблоне блока goods смешаем каждый элемент item с блоком box.

content.push({
    elem: 'item',
    mods: mods,
    mix: [{ block: 'box' }],
    content: ...

https://gist.github.com/4177350

Микс элемента и блока

Товары в блоке box

Декларативный 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

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js