MoBE

Page Content

MoBE

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

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

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

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

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

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

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

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

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

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

Что не так в BEM⁠?

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

BEM + Module = MoBE

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

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

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

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

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

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

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

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

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

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

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

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

Page / Страница
компонент интерфейса представляющий полный перечень целевого функционала всех Модулей на веб-странице⁠, состоит из Модулей⁠, Блоков и Элементов⁠;
Layout / Лейаут
компонент интерфейса не представляющий самостоятельно целевого функционала, включает полезный функционал только за счет включаемой Страницы, может также включать в себя Блоки и Элементы⁠.
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

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

block
    └──block2

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

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

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

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

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

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

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