# BEMHTML: шаблонизатор для БЭМ

**BEMHTML** — шаблонизатор (шаблонный движок) для тех, кто ведет веб-разработку в рамках [БЭМ-методологии](https://ru.bem.info/method).
BEMHTML — это:

  * HTML-верстка в терминах блоков, элементов, модификаторов;
  * поддержка CSS в стиле БЭМ;
  * поддержка JS в стиле БЭМ;
  * декларативные и выразительные шаблоны;
  * удобство сопровождения кода в долгоживущих и развивающихся проектах.

<a name="worldview"></a>
## BEMHTML: картина мира

БЭМ-методология и BEMHTML, как ее часть, обязаны своим появлением опыту верстки и сопровождения веб-интерфейсов в компании Яндекс. Главные условия, определившие характер веб-разработки в Яндексе:

  * задача создания единого портального стиля;
  * необходимость в многолетнем сопровождении кода;
  * потребность быстро вносить изменения;
  * совместная работа над кодом разных специалистов.

БЭМ сформировался как решение всех этих задач, причем прежде всего не как технология, а как *картина мира* — ряд принципов веб-разработки.

BEMHTML — это одна из технологий, позволяющих разрабатывать в соответствии с принципами БЭМ. Поэтому задачи, которые решает BEMHTML, продиктованы этими же принципами.

## BEMHTML: картина мира
За этим принципом стоит убеждение, что HTML и CSS не годятся в качестве удобного фреймворка для семантического описания веб-интерфейсов. Причина этого в том, что браузеры по-разному обрабатывают один и тот же HTML и CSS. В результате приходится включать в HTML и CSS специфические элементы, направленные исключительно на обеспечение совместимости с разными браузерами. Так, в недалеком прошлом для кроссбраузерной реализации скругленных уголков приходилось помещать элемент в структуру из шести вложенных `div`.

Веб-интерфейс следует описывать в высокоуровневых терминах (шапка, кнопка, меню), что позволяет разделить **семантику** и **реализацию**. Такое противопоставление помогает и разделить труд тех, кто разрабатывает структуру интерфейса (дизайн, юзабилити, связь с бэкендом), и тех, кто владеет тонкостями HTML-верстки (кроссбраузерность, поддержка IE).

Преобразование высокоуровневых интерфейсных терминов в низкоуровневый командный язык браузера (HTML+CSS) напоминает компиляцию в *ассемблер для веба*.

**Задача шаблонизатора: разделить логику организации интерфейса и HTML-представление**.

  * Организация интерфейса (набор и расположение элементов на странице) описывается отдельно от шаблонов.
    В рамках БЭМ организацию интерфейса описывает **БЭМ-дерево** — иерархическая структура из блоков, элементов и модификаторов.
  * Шаблоны определяют только правила HTML-оформления отдельных частей интерфейса.
    В рамках БЭМ каждой части интерфейса соответствует **БЭМ-сущность** (блок или элемент с модификаторами).

## BEMHTML: картина мира
Этот принцип не специфичен для веб-разработки и вряд ли вызовет возражения. Тем не менее, технологические особенности многих шаблонизаторов нередко вынуждают дублировать код. Это проявляется во всех ситуациях, когда один и тот же элемент интерфейса (например, кнопка) используется многократно. В большинстве шаблонизаторов HTML-код, описывающий кнопку, придется повторить на всех страницах, где она используется. Когда кнопку потребуется усложнить, разработчику придется отредактировать шаблоны всех страниц, где она присутствует. Даже если шаблонизатор позволяет вынести код кнопки в общую функцию, на всех страницах нужно будет заменить код кнопки вызовом этой функции.

**Задача шаблонизатора: возможность создания гибких библиотек шаблонов**.
Гибкость библиотеки подразумевает:

  * для верстальщика — возможность использовать любой шаблон в качестве блока для построения страниц;
  * и возможность легко модифицировать шаблон в своем проекте без необходимости вмешиваться в код библиотеки;
  * для автора библиотеки — отсутствие необходимости заранее проектировать, что именно можно будет переопределять.

### «БЭМ головного мозга»
BEMHTML представляет собой распространение БЭМ-методологии на еще одну технологию — HTML. БЭМ предлагает дизайнеру, разработчику интерфейса, JavaScript-программисту работать в терминах единой предметной области — блоков, элементов, модификаторов. BEMHTML позволяет HTML-верстальщику присоединиться к ним.

**Задача: инструментарий для верстки в терминах БЭМ**.

  * для каждой БЭМ-сущности (блока, элемента, модификатора) — свой шаблон;
  * интеграция с CSS в стиле БЭМ (автоматическая генерация БЭМ-классов для блоков, элементов, модификаторов);
  * интеграция с JS в стиле БЭМ (автоматическая генерация классов и JS-параметров для блоков, элементов, модификаторов).

## Возможности BEMHTML
### Верстка в терминах БЭМ (блоков, элементов, модификаторов)

  - Структура страниц описывается в формате BEMJSON — JavaScript-представление БЭМ-дерева.
  - Шаблонизатор выполняет преобразование БЭМ-дерева в HTML.
  - Интеграция с CSS в стиле БЭМ.
  - Интеграция с JS в стиле БЭМ.

#### Примеры
Данные (БЭМ-дерево), которые шаблонизатор принимает на вход:

```js
{
  block: 'widgets',
  content: [
    {
      elem: 'weather',
      content: 4
    }
  ]
}
```


Шаблон не требуется (обработка шаблонами по умолчанию).

HTML-результат:

```xml
<div class="widgets">
  <div class="widgets__weather">4</div>
</div>
```


Если блок `weather` имеет JavaScript-реализацию с использованием `i-bem.js`, то для передачи JavaScript-параметров блоку можно использовать шаблон:

```js
block weather, js: { id: Math.random() * 1e4 }
```


HTML-результат:

```xml
<div class="widgets i-bem" onclick="return { 'weather': { 'id': 4321 } }">
  <div class="widgets__weather">4</div>
</div>
```


### Синтаксис описания данных и шаблонов основан на JavaScript
  - Декларативные шаблоны. Шаблон состоит из условий применения (предикат) и структуры данных, описывающей HTML-результат (тело шаблона).
  - Для работы с БЭМ-сущностями синтаксис JavaScript расширен ключевыми словами (`block, elem, mods, elemMods`).
  - Возможность использовать произвольный JavaScript-код в шаблонах. BEMHTML не ставит технических ограничений на выполнение операций в предикате и теле шаблона. Эффективность и корректность работы шаблонов обеспечивается соглашениями, которым следуют разработчики.
  - Все BEMHTML-шаблоны компилируются в переносимый JavaScript, что позволяет выполнять шаблоны как на сервере, так и на клиенте.

#### Примеры
Объявление HTML-тегов для блоков в декларативном стиле:

```js
block widgets {
  tag: 'ul'
  elem weather, tag: 'li'
}
```


HTML-результат:

```xml
<ul class="widgets">
  <li class="widgets__weather">4</li>
</ul>
```


Произвольные вычисления в теле шаблона:

```js
block widgets, elem weather, content: {
  var oldContent = applyNext()
  return oldContent > 0 ? '+' : '' + oldContent + ' °C'
}
```


HTML-результат:

```xml
<ul class="widgets">
  <li class="widgets__weather">+4 °C</li>
</ul>
```


### Гибкость и расширяемость «по построению»
  - BEMHTML позволяет разделить верстку на **независимые блоки**, свободно комбинируемые между собой.
  - **Уровни переопределения** позволяют объединять шаблоны в библиотеки, которые можно использовать в одном или нескольких проектах.

#### Примеры
Входные данные:

```js
{ block: page,
  content: [
    {
      block: header,
      content: 'Заголовок 1'
    },
    {
      block: header,
      mods: { level: 2 },
      content: 'Заголовок 2'
    }
  ]
}
```


Шаблоны, определенные в библиотеке:

```js
block header, tag: 'h1'
block header, mod level 2, tag: 'h2'
```


Шаблоны, определенные в проекте:

```js
block header, tag: 'h2'
block header, mod level 2, tag: 'h3'
```


Результат:

```xml
<div classs="page">
  <h2 class="header">
  Заголовок 1
  </h2>
  <h3 class="header header_level_2">
  Заголовок 2
  </h3>
</div>
```



### Многопроходность
  - BEMHTML позволяет «на лету» генерировать дополнительные элементы, нужные для решения задач верстки — разного рода обертки. В ходе применения шаблонов можно модифицировать исходные данные (БЭМ-дерево) и заново применять те же шаблоны к уже модифицированным данным. Такая многопроходность позволяет реализовать всю логику генерации оберток в шаблонах, не засоряя лишними элементами входные данные.

#### Примеры
Входные данные — имя Яндекс-пользователя:

```js
{
  block: 'ya-user',
  content: 'Хомяк'
}
```


Шаблон — выделяет первую букву в имени пользователя и оборачивает ее в тут же сгенерированный элемент:

```js
block ya-user, content: {
  var oldContent = applyNext();
  return [
    { elem: 'first-letter', content: oldContent[0] },
    oldContent.substring(1)
  ]
}
```


HTML-результат:

```xml
<div class="ya-user"><div class="ya-user__first-letter">Х</div>омяк</div>
```

## BEMHTML и другие шаблонизаторы <a name="andothers"></a>

Шаблонизаторы (шаблонные движки, template engines) — чрезвычайно многочисленный отряд инструментов, насчитывающий сотни видов. За этим многообразием скрывается несколько разных областей применения (генерация HTML, других текстовых форматов, DOM) и несколько различных подходов к решению задачи генерации текста по шаблону и данным.

Не все идеи в разработке шаблонизаторов одинаково популярны. По подходам, которые реализованы в BEMHTML, его можно отнести к разряду «экзотических». Рассмотрим несколько возможных принципов классификации шаблонизаторов и место BEMHTML среди них.

### По семантике (что делает шаблонизатор)
#### Интерполяция строк

Шаблонизатор подставляет в текст шаблона строки, соответствующие нужным данным. В дополнение могут использоваться условия, циклы, переменные. Это самый многочисленный класс шаблонизаторов.

```Шоколад ни в чем не виноват, %username%.```

В качестве примера таких шаблонизаторов можно привести:

* [Mustache](http://mustache.github.com)
* [Handlebars](http://handlebarsjs.com)
* [Jade](https://github.com/visionmedia/jade)
* [DustJS](http://linkedin.github.com/dustjs/)

#### Data bind

Шаблонизатор связывает данные с HTML по правилам, описанным отдельно от HTML.

```js
var html = 'Шоколад ни в чем не виноват,' + '<span class="name"/>.'
, data = { username: 'John Smith' }
, map = plates.Map();

map.class('name').to('username');
plates.bind(html, data, map);
```
Примеры:

* [Transparency](https://github.com/leonidas/transparency)
* [Flatiron.js](http://flatironjs.org/#templating)
* [Pure](http://beebole.com/pure/).

**Live data bind** — отличается тем, что в шаблоне можно декларировать связь не со статическими, а изменяющимися данными. Примеры таких шаблонизаторов — [Angular.js](http://angularjs.org), [Knockout.js](http://knockoutjs.com).

#### Data driven

Шаблонизатор связывает ключевое слово в данных с фрагментом выходного HTML. В предыдущих подходах первичен шаблон, который насыщается данными. Здесь наоборот: первична структура данных, которая при помощи шаблонов транслируется в HTML.

Например, приведенный ниже XSLT-шаблон никак не ограничивает структуру данных, в которой может встретиться ключевое слово:

```xml
<xsl:template match="username">
    <span class="name">
        <xsl:apply-templates/>
    </span>
</xsl:template>
```

С одинаковым успехом его можно применить к таким данным:

```xml
Шоколад ни в чем не виноват, <username>John Smith</username>.`
```

И к таким:

```xml
<ul>
  <li><username>Veni</username>,</li>
  <li><username>Vidi</username>,</li>
  <li><username>Vici</username>!</li>
</ul>
```

Примеры:

* [XSLT](http://www.w3.org/TR/xslt)
* BEMHTML

### По синтаксису
#### Текст в финальном виде со специальными вставками

Эта модель тесно связана с подходом к шаблонизатору как к системе интерполяции строк. Шаблон в этом случае записывается в виде HTML, который очень мало отличается от финального результата.

```xml
<span class="name">[% username %]</span>
```

Отсутствие необходимости изучать специальный синтаксис часто выдвигается в качестве преимущества данного подхода.

Примеры:

* [Mustache](http://mustache.github.com)
* [Handlebars](http://handlebarsjs.com)
* [DustJS](http://linkedin.github.com/dustjs/)

#### Сокращенная запись HTML

Языку HTML свойственна синтаксическая избыточность — необходимость в парных открывающих и закрывающих тегах. Например, чтобы объявить: «здесь таблица» — всегда требуется дважды указать `table` — в открывающем и закрывающем теге. Разаботчики некоторых шаблонизаторов стремятся облегчить написание и восприятие шаблонов, предлагая сокращенный синтаксис для записи HTML-конструкций.

```
span.name #{username}
```

Примеры:

* [Jade](https://github.com/visionmedia/jade)
* [Eco](https://github.com/sstephenson/eco)

#### Предметно-ориентированный синтаксис

При таком подходе в шаблоне описывается не финальная HTML-страница, а правила генерации HTML в соответствии с предметной областью шаблонизатора.

```js
var html = 'Шоколад ни в чем не виноват,' + '<span class="name"/>.'
, data = { username: 'John Smith' }
, map = plates.Map();

map.class('name').to('username');
plates.bind(html, data, map);
```

Примеры:

* [XSLT](http://www.w3.org/TR/xslt)
* [Flatiron.js](http://flatironjs.org/#templating)
* BEMHTML

### По базовому языку

#### Один базовый язык

Шаблонизатор привязан к разработке на определенном языке. На этом языке записываются все управляющие конструкции в шаблонах.

Примеры:

* [XSLT — XML](http://www.w3.org/TR/xslt)
* [ECO — CoffeeScript](https://github.com/sstephenson/eco)
* BEMHTML — JavaScript

#### Компиляция в несколько разных языков

В шаблонизаторе создается своя предметная область, достаточно примитивная, чтобы допускать компиляцию управляющих конструкций в широкий диапазон языков программирования.

Примеры:

* [TT2](http://www.template-toolkit.org/)
* [Mustache](http://mustache.github.com)

### Почему BEMHTML устроен так

  * **Data driven**.
    Привязка шаблонов к элементам данных позволяет делать шаблоны **декларативными** и **атомарными** утверждениями, связывающими элемент и его HTML-представление. Например, *тег блока «шапка» — `table`*. Аналогично CSS в стиле БЭМ описывает оформление атомарными утверждениями: *цвет блока «шапка» — красный*.

    Такие шаблоны обладают модульностью «по построению»: вне зависимости от того, на какой странице встречается ключевое слово, оно будет обрабатываться одним и тем же шаблоном. Это позволяет избежать дублирования кода. Кроме того, атомарные утверждения легко переопределять по отдельности, что открывает возможности для построения гибких библиотек.

  * **Предметно-ориентированный синтаксис**.
    Главные мотивы для выбора синтаксиса, не связанного с HTML, — компактность шаблонов (не нужно повторять каждый тег дважды) и возможность привязывать шаблоны непосредственно к БЭМ-сущностям, а не к тем или иным точкам в HTML.

  * **Базовый язык — JavaScript**.
    Выбор JavaScript в качестве базового языка несет с собой массу преимуществ: производительность, переносимость, активное развитие технологий, и — последнее, но очень важное — привычность для HTML-верстальщиков.

### Почему не XSLT?
В приведенных выше классификациях шаблонизаторов BEMHTML попадает в те же категории, что и XSLT. В Яндексе давно и много использовали XSLT в верстке интерфейсов. Однако разработчики БЭМ отказались от использования XSLT и начали разработку собственного шаблонизатора по следующим причинам:

  * **Проблемы с производительностью**.
    Последовательная реализация модели преобразований, представленной в XSLT, существенно ограничивает его производительность. По скорости работы решения, построенные на XSLT-шаблонах, значительно проигрывают решениям с использованием других шаблонизаторов.

  * **XSLT не развивается**.
    Разработка XSLT фактически не ведется уже несколько лет. Несмотря на то, что в 2007 году опубликована спецификация XSLT 2.0, ее реализации пока далеки от серьезного использования в реальных задачах. В отсутствие активной разработки в части реализации XSLT не приходится ожидать расширения функциональности или повышения производительности.

  * **XSLT не ориентирован на работу в БЭМ-терминах**.
    Для максимальной интеграции шаблонного движка в набор инструментов БЭМ необходима нативная поддержка БЭМ предметной области на уровне шаблонизатора.

## Как попробовать

### Шаблон проекта

Чтобы начать использовать BEMHTML, достаточно склонировать [шаблон проекта](https://github.com/bem/project-stub).

Шаблон содержит подготовленную структуру проекта с подключенной библиотекой блоков [bem-bl](https://ru.bem.info/libs/bem-bl/), настроенным окружением для сборки и просмотра результата и примером очень простой статической страницы.

Фактически шаблон проекта является готовым станком HTML-верстальщика. Его можно наполнять своими блоками и делать на его основе проекты любой сложности.

### Документация

  * [Вводное пошаговое руководство по BEMHTML](https://ru.bem.info/technology/bemhtml/current/intro/)
  * [Справочное руководство по BEMHTML](https://ru.bem.info/technology/bemhtml/current/reference/)
  * [Руководство по bem-tools](https://ru.bem.info/tools/bem/bem-tools/)
  * [Документация библиотеки блоков bem-bl](https://ru.bem.info/libs/bem-bl/)

