Package Exports
- bh
- bh/lib/bh
- bh/lib/bh.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (bh) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
BH
BH — это BEMJSON-процессор. Его главная цель — превратить BEMJSON в HTML.
Зачем нужен BH, если есть BEMHTML?
- BH быстрый. Очень быстрый.
- BH не требует компиляции (в отличие от BEMHTML).
- BH удобен в отладке, т.к. он не компилируется в другой код (в отличие от BEMHTML).
- BH написан на чистом JavaScript, используется и расширяется через JavaScript.
- BH прост для понимания, ведь это всего лишь обертка над обычными преобразованиями исходного BEMJSON в конечный BEMJSON.
- BH компактен на клиенте.
Демо
Посмотреть вот-прям-щас: http://xxx-bh-help.mdevils.alexa.maps.dev.yandex.ru/index.html
Метрики
Скорость обработки BEMJSON (1000 итераций).
BH - 865ms (1156 times per second)
bemhtml - 3103ms (322 times per second)
BEM.HTML - 1356ms (737 times per second)
Вес результирующего файла (сжатого):
BH - 40kb.
BEMHTML - 76kb.
Время сборки.
BH - 50-60ms.
BEMHTML (dev) - 2000-3000ms.
BEMHTML (prod) - 6000-7000ms.
Установка
BH-процессор и ENB-технологии для его использования можно найти в npm-пакете bh
.
npm install bh
Использование
BH-файлы в проекте имеют суффикс bh.js
. Например, b-page.bh.js
. Файл формируется в формате CommonJS для NodeJS:
module.exports = function(bh) {
// ...
};
Преобразования
Функции для работы с BEMJSON ( матчеры ) объявляются через метод match
. В теле функций описывается логика преобразования BEMJSON.
Например, зададим блоку button
тег button
, а блоку input
тег input
:
module.exports = function(bh) {
bh.match('button', function(ctx) {
ctx.tag = ctx.tag || 'button';
});
bh.match('input', function(ctx) {
ctx.tag = ctx.tag || 'input';
});
};
Обратите внимание на конструкцию ctx.tag = ctx.tag || 'button';
. Она написана для того, чтобы не трогать тег в случае, когда он задан явно (в исходном BEMJSON или на другом уровне переопределения).
Теперь нам нужна псевдо-кнопка. То есть, если у кнопки модификатор pseudo
равен yes
, то нужен тег a
и атрибут role="button"
:
module.exports = function(bh) {
bh.match('button_pseudo_yes', function(ctx) {
ctx.tag = ctx.tag || 'a';
ctx.attrs.role = ctx.attrs.role || 'button';
});
};
В данном примере мы матчимся не просто на блок button
, а на блок button
с модификатором pseudo
, имеющим значение yes
.
Матчинг
Рассмотрим синтаксис строки матчинга для функций преобразования:
'block[_blockModName_blockModVal][__elemName][_elemModName_elemModVal]'
По-русски:
'блок[_имяМодификатораБлока_значениеМодификатораБлока][__имяЭлемента][_имяМодификатораЭлемента_значениеМодификатораЭлемента]'
(В квадратных скобках необязательные параметры)
Еще примеры
Например, мы хотим установить модификатор state
со значением closed
для всех блоков popup
:
bh.match('popup', function(ctx) {
ctx.mods.state = ctx.mods.state || 'closed';
});
Замиксуем form
в search-form
:
bh.match('search-form', function(ctx) {
ctx.mix.push({ block: 'form' });
});
Установим класс для b-page
:
bh.match('b-page', function(ctx) {
ctx.cls = ctx.cls || 'i-ua_js_no i-ua_css_standard';
});
Преобразование BEMJSON-дерева
Кроме модификации элемента, функция-преобразователь может вернуть новый BEMJSON.
Например, обернем блок header
блоком header-wrapper
:
bh.match('header', function(ctx) {
return {
block: 'header-wrapper',
content: ctx
};
});
Обернем содержимое button
элементом content
:
bh.match('button', function(ctx) {
ctx.content = {
elem: 'content',
content: ctx.content
};
});
Добавим элемент before
в начало, а after
в конец содержимого блока header
:
bh.match('header', function(ctx) {
ctx.content = [
{ elem: 'before' },
ctx.content,
{ elem: 'after' }
];
});
Добавим блок before-button
перед блоком button
:
bh.match('button', function(ctx) {
return [
{ block: 'before-button' },
ctx
];
});
Утилиты
В bh.utils
содержится ряд вспомогательных методов для работы с BEMJSON:
bh.utils.position()
bh.utils.isFirst()
bh.utils.isLast()
bh.utils.position() возвращает позицию текущего bemjson-элемента в рамках родительского. bh.utils.isFirst() возвращает true, если текущий bemjson-элемент первый в рамках родительского bemjson-элемента. bh.utils.isLast() возвращает true, если текущий bemjson-элемент последний в рамках родительского bemjson-элемента.
Пример:
bh.match('list__item', function(ctx) {
ctx.mods.pos = bh.utils.position();
if (bh.utils.isFirst()) {
ctx.mods.first = 'yes';
}
if (bh.utils.isLast()) {
ctx.mods.last = 'yes';
}
});
bh.utils.extend()
Аналог функции extend
в jQuery.
bh.utils.apply(ctx)
Выполняет преобразования для переданного bemjson-элемента. Может понадобиться, например, чтобы добавить элемент в самый конец содержимого, если в базовых шаблонах в конец содержимого добавляются другие элементы.
ВАЖНО: Эта функция может работать медленно, по возможности избегайте ее.
Пример:
bh.match('header', function(ctx) {
ctx.content = [
ctx.content,
{ elem: 'under' }
];
});
bh.match('header_float_yes', function(ctx) {
bh.utils.apply(ctx);
ctx.content = [
ctx.content,
{ elem: 'clear' }
];
});
bh.utils.generateId()
Возвращает уникальный идентификатор. Может использоваться, например, чтобы задать соответствие между label
и input
.
bh.utils.tParam(key[, value])
Передает параметр вглубь BEMJSON-дерева. Например:
bh.match('input', function() {
ctx.content = {
elem: 'control'
};
bh.utils.tParam('value', ctx.value);
});
bh.match('input__control', function() {
ctx.value = bh.utils.tParam('value');
});