BEMHTML — это шаблонизатор, использующий предметную область «Блок-Элемент-Модификатор». Шаблоны, написанные на BEMHTML, компилируются в plain JavaScript, который может быть выполнен в любом интерпретаторе JavaScript (как на сервере, так и в браузере).
Входными данными для таких шаблонов служит описание страницы в формате JSON, а на выходе получается HTML. Простой пример такого описания страницы находится в тестовом проекте библиотеки блоков. О том, что такое BEMHTML, расказывалось в докладе Сергея Бережного «bemhtml — bem js-шаблонизатор», можно посмотреть видео и почитать презентацию.
BEMHTML шаблоны построены по декларативному принципу (как XSLT), каждый шаблон состоит из двух частей: предикат и тело шаблона.
Предикат – это набор условий, при котороых выполняется шаблон.
Тело – инструкции к выполнению.
Предикат может состоять из одного или нескольких условий. Например:
block b-menu, elem item, elemMod state current, tag:
Запись через запятую означает, что шаблон применим только тогда, когда выполнятся все условия.
При помощи этих условий можно выразить совпадение со входным BEM деревом. Например, можно описать, что шаблон применим к конкретному блоку.
Именами и значениями BEM сущностей в таких условиях могут быть также JS выражения:
block 'b' + '-' + 'link'
block b-menu, elem ['i', 't', 'e', 'm'].join('')
Для каждого блока может понадобиться несколько шаблонов: для тела, для атрибутов, для контента. Поэтому есть необходимость вводить понятие мод. Наличие разных мод позволяет написать несколько шаблонов для одной и той же BEM сущности, каждый из которых продуцирует свою часть выходного HTML этой BEM сущности.
block b-link { ... js: true ... }
block b-icon { tag: 'img' ... }
block b-icon { ... attrs: { return {}; } }
block b-layout-table, elem gap { ... content: { elem: 'gap-i', tag: 'i',} }
Произвольные условия учитывают совпадения с данными, не попадающими под предметную область BEM. Например:
block b-icon { ... attrs, this.ctx.url: { return { src: this.ctx.url }; } }
Здесь this.ctx — хеш с данными контекстной сущности из BEMJSON, в данном случае — блока b-icon.
Последнее из условий предиката отделено от тела шаблона двоеточием:
block b-icon, attrs, this.ctx.url: { Тело шаблона }
block b-icon { tag: 'img' ... }
block b-layout-table, elem gap { ... content: { elem: 'gap-i', tag: 'i' } }
block b-icon { ... attrs: { var ctx = this.ctx, a = { src: '//yandex.st/lego/_/La6qi18Z8LwgnZdsAr1qy1GwCwo.gif', alt: '' }, props = ['src', 'alt', 'width', 'height'], p; while(p = props.shift()) ctx[p] && (a[p] = ctx[p]); return a; } }
Все шаблоны выполняются в каком-либо контексте. Контекст выражается ключевым словом this, доступным как в предикатах, так и в теле шаблона.
this.block === 'b-link'
Это идентично записи условия на языке BEM сущностей
block b-link
Если у нескольких шаблонов часть предиката совпадает, их можно объединить, используя фигурные скобки.
Например, так можно описать элемент item блока в b-menu:
block b-menu { elem item { Здесь всё про b-menu__item } }
То же самое можно написать через запятую. Вот так:
block b-menu, elem item { Здесь всё про b-menu__item }
То есть, скобки — это syntax sugar над запятыми. Вложенность условий может быть сколь угодно большой.
local — это блок кода языка BEMHTML, по синтаксису подобный блокам while и for из JavaScript.
local(expressions) code
local(expressions) { code }
local используется для временного изменения контекста и переменных, также для последующих операций с ними. В круглых скобках могут быть описаны только выражения присваивания, в фигурных — JavaScript код, который выполняется с учётом заданных изменений. По выходу из блока local все переменные и объекты (в том числе и this.ctx) приобретают свои прежние значения. Более подробную информацию можно найти в документации xjst.