БЭМ-методология: с чего всё начиналось и зачем это всё нужно
На Хабре уже много писали о методологии БЭМ, выросшей в Яндексе. И мы решили, что пора системно рассказать о том, откуда она появилась и что сделало БЭМ таким, каким мы его знаем. Думаем, это будет интересно не только тем, кто уже использует БЭМ, но и тем, кто считает, что эта методология не подходит для их проектов. Возможно, они увидят, что мы решали проблемы, похожие на их собственные, и найдут что-то полезное для себя.
Конечно, все началось с собственных потребностей Яндекса. Вместе с тем, как он рос, росло и количество сотрудников, которые занимаются фронтендом. Постепенно команда увеличилась настолько, что стало очевидно — без единых стандартов работать будет сложно. К тому же, мы находимся в офисах Яндекса в разных городах. Возникла идея создать общую методологию, которая поможет организовать процессы в большой команде, работающей над разными проектами. А главное то, что мы хотели не только упорядочить и ускорить разработку, но и снизить порог входа в проект для нового разработчика.
Для чего нужна БЭМ-методология
Какие требования мы сформулировали:
- Разработчик должен понимать свой код (даже вернувшись к нему через год) и код любого программиста в команде БЭМ-проекта.
- Любой блок кода может быть использован повторно: необходимо создать общую базу знаний и не писать каждый раз всё с нуля, а использовать готовые наработки.
- Работая в одной команде, разработчики, менеджеры, дизайнеры и верстальщики должны называть одни и те же вещи одинаково. То есть говорить на одном языке.
- Команды могут обмениваться специалистами для реализации какой-то конкретной функциональности.
- Порог входа при переходе на новый проект должен быть снижен за счет одинаковой структуры организации всех БЭМ-проектов и одинаковых правил именования всех сущностей.
Мы стремились к тому, чтобы с увеличением числа разработчиков улучшалось и качество продукта. Это значит, что разработчики должны быть в курсе работы друг друга и не изобретать заново то, что уже реализовано. Мы хотели создать единую команду, которая работает над разными проектами.
История развития БЭМ
Как верстали 10 лет назад
Но когда все начиналось, ни о каких компонентных подходах и модульности в веб-разработке речи не шло. Все верстали сайты, складывая CSS в один файл project.css
, скрипты, которых было очень мало, — в project.js
, а картинки — в папку images
.
В 2005 году обычный, с точки зрения интерфейса, проект был набором статических HTML-страниц. Вот такой была типичная структура проекта того времени:
about.html # Для каждой страницы создавался отдельный HTML-файл
index.html
…
project.css # Стили находились в одном файле для всего проекта
project.js # Скрипты хранились в одном файле для всего проекта
images/ # Картинки складывались в отдельную директорию
yandex.png
В CSS использовались id
, классы и теги.
Пример
#foot div div div div
{
background-position: 54%;
background-image: url(../i/foot-4.png);
}
Типичный CSS того времени в большинстве случаев содержал длинный каскад.
Малейшие изменения требовали длительного рефакторинга. Свёрстанные статические HTML-страницы нарезались в шаблоны. Если HTML изменялся, все правки было необходимо переносить вручную в шаблон.
Вёрстка в больших проектах была неуправляемой.
Основы БЭМ-методологии
Технологии (HTML, CSS, JavaScript), которые мы использовали, изменялись в зависимости от требований проекта, а принципы БЭМ должны были быть универсальны.
Мы сформулировали основные правила, по которым будут жить и развиваться наши проекты, и которые никак не будут зависеть от технологий и инструментов.
Чтобы ускорить разработку, необходимо было облегчить поддержку HTML и CSS отдельных компонентов страницы, сделать код менее связанным. Для этого мы разбили страницу на части. Так появилось новое понятие — блок. Блок мог состоять из различных элементов, которые не использовались вне самого блока. Состояния и поведение блока и элемента можно было задавать с помощью модификатора.
Это были три ключевых понятия, на которых основывалось большинство правил. Аббревиатура от трех слов Блок, Элемент и Модификатор стала названием методологии — БЭМ.
Блок
Логически и функционально независимый компонент страницы. Блок полностью самодостаточен: у него может быть свое поведение, шаблоны, стили, документация и не только. Блоки могут использоваться в любом месте страницы, повторно, даже в другом проекте.
Одни блоки можно вкладывать в другие, компоновать, использовать для создания более сложных блоков.
Элемент
Часть блока, которая не может использоваться в отрыве от него и имеет смысл только в рамках своего родителя. Элементы могут быть обязательными и опциональными.
Работая с элементами, важно помнить правило: не рекомендуется создавать элементы элементов. Если вложить один элемент в другой, будет невозможно изменить внутреннюю структуру блока: элементы нельзя будет поменять местами, удалить или добавить без корректировки существующего кода.
Модификатор
Свойство блока или элемента, которое меняет их внешний вид, состояние или поведение.
Модификатор имеет имя и может иметь значение. Использование модификаторов опционально. У блока/элемента может быть несколько разных модификаторов одновременно.
Так, например, с помощью модификатора можно изменить не только цвет меча, но и его функциональность (как показано в случае с красным мечом):
Правила именования CSS-селекторов
Все принципы БЭМ формировались и внедрялись постепенно. Мы начали с того, что сформулировали жесткие правила именования CSS-селекторов.
По БЭМ-методологии блоки не уникальны и их всегда можно использовать повторно, поэтому в описании CSS-правил отказались от использования id
.
Блок не должен зависеть от окружающих его блоков и сам не должен влиять на соседние блоки, поэтому в CSS отказались от:
- тегов;
- вложенных селекторов;
- глобального сброса правил для всей страницы.
Важной определяющей сущностью в именовании селекторов стал блок:
- Полное имя элемента/модификатора формируется так, чтобы из него можно было определить принадлежность данного элемента/модификатора к конкретному блоку.
- По имени модификатора элемента должно быть возможно определить принадлежность данного модификатора к конкретному элементу конкретного блока.
Правила формирования имени БЭМ-сущности
- Каждая БЭМ-сущность должна иметь свой класс.
- CSS-свойства для блоков, элементов и модификаторов описываются только через классы.
- Для разделения слов в именах используется дефис (
-
). - Элемент отделяется от блока двумя подчеркиваниями (
__
). Модификатор — одним (_
). - Имена БЭМ-сущностей записываются с помощью цифр и латинских букв в нижнем регистре.
Мы долго экспериментировали с префиксами в именах, но в итоге отказались от них.
Пример
- Имя блока —
header
. - Имя элемента блока —
header__search-form
— элементsearch-form
блокаheader
. - Имя модификатора блока —
header_theme_green-forest
— модификаторtheme
в значенииgreen-forest
блокаheader
. - Имя модификатора элемента —
header__search-form_disabled
— булев модификаторdisabled
элементаsearch-form
блокаheader
.
HTML
...
CSS
.header { color: red; }
Существует ряд альтернативных схем именования. Выбор всегда остается за вами.
Но мы рекомендуем придерживаться описанной выше схемы, так как инструменты БЭМ-платформы умеют работать именно с данным вариантом именования.
БЭМ в HTML
Мы хотели упорядочить HTML и в итоге пришли к тому, что больше не пишем HTML руками. Подробнее читайте в разделе про описание инструментов БЭМ.
В HTML каждая БЭМ-сущность определяется своим классом.
...
В простейшем случае блок соответствует DOM-узлу, один к одному. Но DOM-узел и блок — это не всегда одно и то же. На одном DOM-узле может совмещаться несколько сущностей. Это называется миксом.
С помощью миксов можно:
- объединять поведение и стили нескольких БЭМ-сущностей без дублирования кода;
- создавать семантически новые компоненты интерфейса на основе имеющихся блоков, элементов и модификаторов;
- задавать позицию вложенного блока в родительском, не создавая дополнительных модификаторов. Подробнее, о том, как создавать обёртки в HTML, читайте на форуме.
Пример
В проекте кнопки реализованы блоком button
. Необходимо поместить кнопку в форму поиска (search-form
) и задать для кнопки отступы. Для этого воспользуемся миксом блока button
и элемента button
блока search-form
:
Микс позволяет использовать универсальную кнопку, которая ничего не знает об отступах от границ конкретной формы. В данном случае в форме поиска есть элемент search-form__button
, который знает, где ему надо находиться, и блок button
, который нужно отображать.
Вместо микса можно создать дополнительный модификатор блоку button
, но мы не рекомендуем этот способ, так как позиционирование блока button
по смыслу не является частью универсального блока, а подходит только для его конкретного места использования.
Организация файловой системы
Нас не устраивала первоначальная структура проекта в файловой системе: в ней было сложно ориентироваться и находить нужные технологии сущностей.
Что мы хотели получить от новой структуры:
- Унифицированную файловую систему любого БЭМ-проекта.
- Универсальную расширяемую структуру репозитория. При добавлении в проект дополнительной технологии заранее известно, где будут находиться новые файлы.
- Быстрый поиск по файловой системе проекта.
- Повторное использование кода.
- Неограниченную возможность переноса кода всего блока из проекта в проект.
Сначала мы попробовали разделить репозиторий проекта по технологиям:
css/
html/
js/
xml/
xsl/
Такой подход не показал кардинальных изменений. Поэтому мы вынесли общую часть кода, подходящую для всех проектов и платформ, в отдельную директорию common
. Специфические реализации, необходимые только определённым проектам, складывали отдельно — в директорию service
. А примеры — в директорию example
.
common/
css/
js/
xml/
xsl/
example/
html/
service/
auto/
css/
xml/
Так мы быстрее находили нужный код для отдельных проектов. Но эта структура всё равно не отвечала всем нашим требованиям.
Блоки первичны, технологии — вторичны
Чтобы создать нужную нам структуру проекта и реализовать наши цели, которые мы перед собой ставили, мы вывели на передний план блоки, а не технологии.
Блок в файловой системе полностью независим: все технологии, необходимые для его реализации, находятся в директории этого блока.
Чего мы добились:
Ускорения разработки
- Блоки могут использоваться повторно.
- Реализация блоков может быть изменена на новом уровне переопределения, не затрагивая при этом базовую функциональность и стили.
- Блок — независимый компонент страницы и в его директории находится всё, что необходимо для корректного функционирования. Поэтому блоки легко переносить из проекта в проект, достаточно просто скопировать директорию блока.
Ускорение рефакторинга
- Разработчик работает c небольшими блоками кода.
- Технологии реализации одного блока не связаны с технологиями другого.
- Одинаковая структура репозитория позволяет быстро ориентироваться в проекте и находить нужные файлы.
Универсальная расширяемая система
- Появились уровни переопределения.
- Количество технологий не ограничено. Любая новая технология реализации находится в файле конкретного блока. Так, когда мы создавали новую файловую структуру, мы не планировали писать unit-тесты на JavaScript. Но, когда появилась такая необходимость, мы знали, где разместим эти файлы в проекте.
Технологии реализации
Придумали новый термин — технология реализации.
Блоки могут выполнять разные функции на странице. В зависимости от предназначения блока может меняться его реализация. Под реализацией в БЭМ понимают поведение, внешний вид, шаблоны, документацию к блоку, все виды тестов, картинки и так далее.
Для реализации блока используются различные технологии, например:
- поведение — JavaScript, CoffeeScript;
- внешний вид — CSS, Stylus, Sass;
- шаблоны — Jade, Handlebars, XSL, BEMHTML, BH;
- документация — Markdown, Wiki, XML.
Выбор технологий реализации не ограничен, разве только требованиями вашего проекта.
В новой организации файловой структуры каждая технология реализации представляет собой отдельный файл с соответствующим расширением. Все файлы реализации блока хранятся в директории этого блока.
Всё в проекте перестраивается относительно этого нового принципа. Блок становится ключевым понятием БЭМ. Соответственно, изменяется и структура файловой системы.
Правила организации файловой системы БЭМ-проекта
- Блок — отдельная директория в файловой системе. Имя блока и его директории совпадают.
- Реализация блока разделяется на отдельные файлы.
- Файлы, относящиеся к блоку, всегда находятся в его директории.
- Опциональные элементы и модификаторы выносятся в отдельные файлы.
- Проект разделяется на уровни переопределения.
Пример
blocks/
input/ # Директория блока input
_theme/ # Директория опционального модификатора theme
input_theme_forest.css # Реализация модификатора theme в значении forest в технологии CSS
__clear/ # Директория опционального элемента clear
input__clear.css # Реализация элемента clear в технологии CSS
input__clear.png # Реализация элемента clear в технологии PNG
input.css # Блок input в технологии CSS
input.js # Блок input в технологии JavaScript
button/ # Директория блока button
button.css
button.js
button.png
Уровень переопределения
Уровнем переопределения мы стали называть директории с реализацией блоков. Появление уровней позволило изменять реализацию блока, добавляя новые свойства (доопределять) или изменяя старые (переопределять) на другом уровне. Конечная реализация блока собирается со всех уровней последовательно.
Уровни переопределения позволяют:
- Подключать библиотеки и обновлять их, не делая правок в коде.
- Выделять общие части реализаций блоков на один уровень, а частные случаи (например, специфическую реализацию для отдельных сервисов) — на другой.
- Разделять проект на платформы. Общую реализацию для всех платформ хранить на одном уровне, а платформо-специфичную — выносить на другой.
- Избежать копирования кода и создания новых сущностей, если необходимо изменить уже существующую функциональность.
Если сравнить уровни со слоями, то базовый слой — это исходная реализация блока, а каждый последующий слой накладывается сверху и дополняет (наследует) или изменяет базовую реализацию.
Пример
project/ # Уровень проекта
input/ # Измененная реализация блока input
button/
header/
library-blocks/ # Уровень библиотеки
input/ # Базовая реализация блока input
button/
popup/
Как начать работать с БЭМ
Как вы могли заметить, наша команда тоже начинала работу с БЭМ постепенно. Гибкость БЭМ-методологии позволяет настраивать её под ваши текущие процессы.
Не существует универсального метода, чтобы начать применять методологию в своем проекте. Каждая конкретная команда встраивает его в процесс разработки и использует так, как ей удобно.
Например, у вас есть проект, в котором вы хотите применить БЭМ только для вёрстки. Вы используете в проекте CSS и HTML, значит можно начать с правил именования CSS-селекторов. Это самый распространенный способ использования БЭМ-методологии. Многие команды начинают именно с него. Мы тоже с этого начинали.
По мере внедрения новых правил возникает потребность в собственных инструментах и технологиях.
БЭМ и технологии
В веб-разработке финальный продукт состоит из разных технологий (например, HTML, CSS, JavaScript). Основной принцип БЭМ-методологии — использовать единые термины и подходы к реализации во всех применяемых технологиях.JavaScript
Чтобы работать в БЭМ-терминах и писать декларативно JavaScript, который можно разделять по уровням переопределения, нам понадобился собственный фреймворк — i-bem.БЭМ-дерево
Типичная веб-разработка сводилась к тому, что мы писали HTML, затем нарезали его на шаблоны. При изменении дизайна приходилось менять HTML и шаблоны вручную.
Чтобы избавиться от ручной работы, мы добавили новый уровень абстракции — БЭМ-дерево, которое позволяет работать со структурой веб-страницы в терминах блоков, элементов и модификаторов. БЭМ-дерево — это абстракция над DOM-деревом.
БЭМ-дерево описывает все БЭМ-сущности, которые используются на странице, их состояния, порядок, вложенность. Оно может быть выражено любым форматом, который поддерживает древовидную структуру, например, XML или JSON.
Пример
Рассмотрим пример DOM-дерева:
Ему соответствует такое БЭМ-дерево:
header
logo
search-form
input
button
lang-switcher
Его можно сравнить с шаблонизатором Jade, но отличие в том, что мы пишем не HTML, а используем абстракции.
Это же БЭМ-дерево будет иметь следующий вид в форматах XML и BEMJSON:
XML
BEMJSON — JavaScript-формат, который позволяет работать в БЭМ-терминах. BEMJSON позволяет абстрагироваться от HTML-разметки и описывать страницу в терминах блоков, элементов и модификаторов.
{
block: 'header',
content : [
{ block : 'logo' },
{
block : 'search-form',
content : [
{ block : 'input' },
{ block : 'button' }
]
},
{ block : 'lang-switcher' }
]
}
Мы описываем страницу, которую хотим получить в браузере в виде БЭМ-дерева и не пишем HTML руками: шаблонизатор BEMHTML обрабатывает BEMJSON и генерируют HTML.
БЭМ и инструменты
Чтобы работать со всеми технологиями в удобном для разработчика виде, мы разделили проект на множество отдельных файлов. Это дало нам описанные выше преимущества. Но нам понадобилась сборка и оптимизация, чтобы созданный код мог работать в браузере.
Собирать все файлы вручную неудобно, мы начинаем автоматизировать большинство повторяющихся процессов. Появляются bem-tools — набор инструментов для работы с файлами по БЭМ-методологии. Позже на смену bem-tools пришел ENB.
Чтобы иметь возможность собрать разрозненные файлы, ничего не знающие друг о друге, используется технология DEPS, которая указывает зависимости одного блока от другого или от набора блоков.
Инструменты БЭМ направлены на то, чтоб разработчик писал код так, как ему удобно, а оптимизацией и подключением в проект только нужных файлов в правильном порядке занимались роботы.
БЭМ и библиотеки
Многие БЭМ-библиотеки можно найти в open source. Базовыми являются:
- bem-core — базовая библиотека блоков, которая содержит JavaScript-фреймворк
i-bem
и 20 блоков-хелперов для разработки по БЭМ-методологии. - bem-components — универсальная библиотека готовых визуальных компонентов (блоков). Содержит контролы форм и другие базовые компоненты для построения интерфейсов.
Библиотеку bem-components можно подключать по аналогии с Bootstrap: добавить предварительно собранные файлы библиотеки и вставить их в HTML страницы с помощью элементов и
.
Такой способ поставки называется Dist и включает предсобранный CSS- и JavaScript-код и шаблоны. С ним вам не потребуется инструменты для сборки или шаблонизаторы — блоки заранее собраны и работают.
О том, как подключать файлы с CDN или локально, использовать bower или самостоятельно собрать файлы библиотеки из исходников, читайте в описании библиотеки.
Заготовка проекта
Быстро начать разработку БЭМ-проекта можно с помощью project-stub — проекта с заранее предустановленными технологиями и инструментами. Начинать знакомство с ним стоит с помощью быстрого старта по БЭМ.
Расширенный пример использования project-stub описан в документе Создаем свой проект на БЭМ.
И в заключение
БЭМ-методология — это набор правил и рекомендаций по организации работы над проектом.
В какой-то момент мы отделили методологию от ее практической реализации — платформы.
БЭМ-платформа — это частный случай реализации общих принципов БЭМ-методологии. Так как все технологии создавались с учетом требований наших проектов и развивались постепенно, БЭМ-платформа наиболее полно охватывает все возможности, которые предоставляет БЭМ-методология. Более подробно о ней можно прочитать здесь.
Все части БЭМ-платформы интегрированы для совместной работы, но могут быть использованы и по отдельности. Каждая часть решает конкретную задачу и её можно настраивать под свой процесс и заменять на другие.
Выбирайте наиболее подходящий способ для вашего проекта, экспериментируйте. Можно даже выкладывать свои проекты сайт. Мы будем благодарны вам за обратную связь.