UNPKG

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
# 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/)