На главную

MoBE — почти БЭМ

Методология MoBE

Занудный взгляд на БЭМ, размышления о том, что есть в БЭМ и о том, чего ей не хватает, с картинками и аргументами.

Публикация от . Крайнее обновление .

Методология BEM

БЭМ — воплощение принципа Абсолютно Независимого Блока. Все его сущности должны быть переносимыми и независимыми от других сущностей страницы без прямого на то указания. Согласно методологии БЭМ:

Block / Блок
Логически и функционально независимый компонент страницы, аналог компонента в Web Components. Блок инкапсулирует в себе поведение (JavaScript), шаблоны, стили (CSS) и другие технологии реализации.
Element / Элемент
Составная часть блока, которая не может использоваться в отрыве от него.
Modificator / Модификатор
БЭМ-сущность, определяющая внешний вид, состояние и поведение блока или элемента.

Если проще, то Блок — это более-менее целостный компонент страницы, с описанными выше признаками, Элемент — его составная часть, а Модификатор — CSS-сущность модифицирующая / дополняющая стили Элемента.

БЭМ-структура «Блок-Элемент»
БЭМ-структура «Блок — Элемент»

В БЭМ Блоком могут являться и логотип, и форма авторизации, и шапка сайта.

Пример разделения страницы на Блоки по БЭМ
Пример разделения страницы на Блоки по БЭМ

Если по ходу прочтения статьи вы обнаружили расхождение с актуальной версией методологии БЭМ, пишите — будем вместе править и дополнять.

Что не так в BEM?

Этот вопрос мучает меня пару лет. И, знаете, мой ответ, похоже, вас удивит:

БЭМ прекрасен!

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

В методологии БЭМ, есть упоминание принципа разделения компонентов интерфейса по функциональному признаку. Он находит отражение в определении Блока (данное определение актуально на 02 ноября 2015 года):

Блок — логически и функционально независимый компонент страницы

Во-первых, меня смущает фраза «логически независимый»: «логически» в интерфейсе или в функциональной плоскости?

Конечно, нельзя говорить о пользовательском интерфейсе программного обеспечения в отрыве от функциональности самого ПО, ибо интерфейс — это и есть средство управления / общения с данным функционалом ПО, в частности, и программным обеспечением, в целом. Но вопрос, всё же, остаётся.

Во-вторых, Блок не просто «логически независимый», он «логически и функционально независимый компонент страницы». Тогда какая именно «логическая независимость» предполагалась авторами? И функционально независим от чего? Автор, конечно же, имеет в виду независимость от соседних / сестринских компонентов страницы.

Необходимо более явно обозначить признак разделения компонентов интерфейса.

Да, я откровенно раздуваю маленькие неоднозначности в определениях. Но ведь четкое и понятное формулирование терминов — это залог правильного пониманию всей методологии. Допускаю, что для многих эти вопросы существуют, как и для меня.

Зачастую, при попытке отнесения компонента к одному из типов сущностей БЭМ, были доводы как в пользу Блока, так и в пользу Элемента.

Путешествие веб-разработчика по методологии БЭМ
Путешествие веб-разработчика по методологии БЭМ

Помню, что несколько лет назад, в документации БЭМ, определение Блока было иным. Там не было упоминания Web Components, инкапсуляции и логической независимости. А было вот что:

Block / Блок
Некая самостоятельная сущность, кирпичик проекта. Блок содержит информацию о самом себе и может знать о своих детях — элементах блока. Они могут использоваться сами по себе или внутри других блоков.
Element / Элемент
часть блока, которая отвечает за какую-то отдельную функцию. Элементы блока имеют смысл только в рамках своего родителя. Могут быть обязательными и не обязательными.
Modificator / Модификатор
это свойство блока или элемента, которое меняет внешний вид или поведение. Модификатор имеет имя и значение. Одновременно может использоваться несколько разных модификаторов.

В терминах упоминался функциональный признак, но теперь только для Элемента. В целом, признак разделения также был туманным.

Общей, однозначной и бесспорной является идея наличия подчинённости «Блок—Элемент».

Иерархия компонентов интерфейса по БЭМ
Иерархия компонентов интерфейса по БЭМ

БЭМ-дерево описанной структуры:

block
    ├──block__element
    ├──block__element
    └──block__element
    └──block2

Данная подчинённость безумно проста и тем прекрасна. Реализация этой идеи позволяет вывести код из состояния «лапшички» в куда более структурированный, как внутри файла, так и на уровне структуры проекта на файловой системе.

А что нужно-то еще? БЭМ хорош: оброс богатым инструментарием, поддерживается достаточная для использования документация, существует большое сообщество.

Но в своей сути БЭМ порой неоднозначен, мне хочется видеть в методологии более четкий признак деления компонентов интерфейса по типам.

Анализируя свой код, я раз за разом обнаруживаю папки layout, pages, modules, UI, content и т.п. Если по существу, то хочется более осмысленной системы упорядочивания кода согласно какому-либо очевидному признаку.

Я согласен с разработчиками БЭМ, что главным признаком тут должен стать функциональный признак, упоминаемый в определении Блока в текущей редакции и в определении Элемента прошлого образца.

Одна из идей заключается в том, чтобы трансформировать связь «Блок—Элемент» в связь «Родитель—Потомок», где

Родителем может быть любой компонент интерфейса стоящий выше по иерархии, согласно Методологии.

А за Элемент принимаем любой DOM-элемент, находящийся в непосредственной подчинённости своего Родителя, прежде всего Блока.

Более общая иерархия компонентов интерфеса
Более общая иерархия компонентов интерфейса
parent
    ├──parent__child
    ├──parent__child
    └──parent__child

Таким образом схема «Блок — Элемент» «Родитель — Потомок» становится более прозрачной и очевидной для применения.

Ниже, представляю вашему вниманию видение разделения веб-страницы на компоненты пользовательского интерфейса. Речь не о замене БЭМ, а о его дополнении.

BEM + Module = MoBE

По БЭМ веб-страницу разделяют на следующие компоненты интерфейса: Блок и Элемент.

В ходе своей деятельности в качестве разработчика, у меня появился некий диалект методологии БЭМ, назвал я его MoBE (Модуль-Блок-Элемент).

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

Я использую бОльшее количество типов компонентов интерфейса, согласно, прежде всего, их функционального предназначения, вот главные из них:

Module / Модуль
Компонент интерфейса комплексно решающий задачу пользователя за счёт имеющегося функционала. Модуль состоит из Блоков и Элементов. Модуль не может включать другие Модули.
Block / Блок
Компонент интерфейса, реализующий часть функционала Модуля. Блок состоит из Элементов. Блок может включать другие Блоки. Экземпляры Блока могут повторяться.
Element / Элемент
Компонент интерфейса, DOM-элемент. Является составной частью компонентов интерфейса других типов.

Важно отметить, что вывод полезной информации — это тоже функционал приложения.

Модуль, является сутью интерфейса, является средством в работе пользователя для достижения цели.

MoBE-структура компонентов интерфейса «Модуль — Блок»
MoBE-структура компонентов интерфейса «Модуль — Блок»

Блок несет в себе функционал необходимый пользователю для решения конкретной пользовательской задачи в рамках Модуля.

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

И Элемент, сам по себе, в отрыве от Блока и Модуля, не нужен пользователю. Элемент — это атомарный DOM-элемент, лишённый всякого функционала полезного пользователю.

MoBE-структура компонентов интерфейса «Блок-Элементы»
MoBE-структура компонентов интерфейса «Блок — Элементы»

В качестве рекомендации предлагаю, в дополнение, использовать другие сущности: Страница и Лейаут.

По БЭМ, данные сущности являются Блоками, и имеют реализацию через префиксы: линк, линк, линк.

Page / Страница
Компонент интерфейса представляющий полный перечень целевого функционала всех Модулей на веб-странице. Страница состоит из Модулей, Блоков и Элементов.
Layout / Лейаут
Компонент интерфейса не представляющий самостоятельно целевого функционала. Включает полезный функционал только за счет включаемой Страницы. Может также включать в себя Блоки и Элементы.
MoBE-структура «Страница-Модуль»
MoBE-структура «Страница — Модуль»

Страница включает в себя все Модули. Блоки и Элементы могут включаться на всех уровнях иерархии компонентов интерфейса.

MoBE-структура «Лейаут-Страница»
MoBE-структура «Лейаут — Страница»

Знакомая нам картинка из документации БЭМ принимает вид:

Пример разделения страницы на Блоки по MoBE в сравнении с БЭМ
Пример разделения страницы на Блоки по MoBE в сравнении с БЭМ

Шапка сайта, сама по себе, не обладает какой-либо функциональностью, функционалом обладают Блоки внутри неё.

MoBE-дерево компонентов страницы выглядит, примерно, следующим образом:

layout
    ├── page
    |    ├── module
    |    ├── module
    |    |    ├── block
    |    |    └── block
    |    |        └── block
    |    ├── block
    |    └── block
    |        └── block
    ├── block
    └── block
        └── block,

где layout, page, module и block — сущности в терминах MoBE.
*Элементы в схеме не отражены, но они существуют.

Отражение Элементов в MoBE-дереве не имеет смысла: Элементы не существуют без Блока, определяя Блок мы заведомо определяем Элементы. Да и учитывая, что Элемент в терминах MoBE — это DOM-элемент, MoBE-дерево превратится в DOM-дерево.

А структура шаблонов проекта выглядит, примерно, так:

mySuperDuperProject__Views
    ├── layouts
    ├── page
    └── modules
    └── blocks,

где
layouts — папка с Лейаутами,
page — папка с общими (конструктивными) Блоками Страниц,
modules — папка с Модулями,
blocks — папка с Блоками

Контекст иерархии

Стоит обратить внимание, что выше, описывая связи MoBE-сущностей, я не упомянул про DOM-структуру данных связей. На самом деле, я рекомендую расширять один Блок на другой Блок через Элемент первого, т.е. используется промежуточный DOM-элемент, который является Элементом расширяемого Блока.

Иерархия компонентов интерфейса по БЭМ
Иерархия компонентов интерфейса по MoBE

MoBE-дерево описанной структуры:

block
    └──block2

DOM-дерево описанной структуры:

block
    ├──block__element
    ├──block__element
    └──block__element
    └──block__element
        └──block2

Мы можем прозрачно влиять на представление расширяющего Блока (положение, размеры) в контексте Родителя через Элемент расширяемого Блока.

Цель — сохранить расширяющий Блок в исходном виде.

Задав положение, поведение и размеры области доступной для расширяющего Блока, мы определяем представление самого Блока в контексте Родительского Блока без модификации.

Дело в том, что Элемент одного Блока вкупе с вложенным Блоком создаёт новый контекст иерархии. Данное соглашение позволяет сохранять прозрачность связи «Родитель — Потомок».

А вот смешивать Элемент и Блок на одном DOM-элементе не рекомендую по описанным выше причинам.