bem-md-renderer
Version:
The builder markdown to html with custom renderer, which generates the html headlines like github
430 lines (293 loc) • 27.1 kB
Markdown
# 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/)