Методологию БЭМ придумали в Яндексе больше десяти лет назад. За это время фронтенд сильно изменился: появились компоненты, фреймворки, утилитарные классы и прочее. Казалось бы, БЭМ должен был уйти в архив. Но нет — он до сих пор спасает проекты от CSS-хаоса. Особенно когда речь заходит о Vue.

Почему? Потому что Vue думает компонентами, а БЭМ думает независимыми блоками. Они не конфликтуют. Они дополняют друг друга. Если у вас уже есть понятная, проверенная система — не стоит изобретать велосипед ради «современного» подхода. Архитектура важнее моды.
Прежде чем переходить к коду, давайте быстро вспомним, что такое БЭМ. Без занудства.
Три кита БЭМ
-
Блок — самостоятельный компонент. Он не должен зависеть от контекста: никаких
marginснаружи, никаких привязок к родительским стилям. Пример:.header,.card,.button. -
Элемент — часть блока, которая не существует отдельно. Имя пишется через двойное подчёркивание:
.button__icon,.card__title. -
Модификатор — состояние или вариант блока/элемента. Пишется через одно подчёркивание:
.button_disabled,.button_size_l,.card_theme_dark.

Как это работает во Vue
Во Vue стили обычно пишут прямо в компоненте. И тут начинается главный вопрос: scoped или нет? :deep() или нет? Один <style> или несколько?
Правило простое, но его часто нарушают:
-
Всегда используйте
scoped, если стили относятся только к этому компоненту. Это изолирует зону ответственности. -
:deep()— не зло, но это «аварийный выход». Применяйте его только когда работаете с сторонними UI-библиотеками или когда без него действительно никак. Не используйте его, чтобы обойти продуманную структуру.
Архитектура ломается не из-за технологий, а из-за решений «сейчас по-быстрому». БЭМ как раз заставляет думать на шаг вперёд.
Разбираем на практике
Допустим, нам нужна кнопка. Она может быть основной, ссылкой, аватаром. Включаться/выключаться. Менять размер и цвет. Без системы это превращается в кашу:
<template> <button :class="{ 'button-basic': isBasic, 'button-href': isHref, 'button-avatar': isAvatar }"> {{ text }} </button></template>
Что не так?
-
Классы не связаны логически.
button-avatar— это вообще состояние, тип или вариант размещения? -
При добавлении новых состояний (
loading,disabled,size-xl) маппинг разрастается. -
Через полгода никто не вспомнит, какой класс за что отвечает. А править стили будет страшно.
Теперь тот же компонент, но с БЭМ:
<template> <button :class="[ 'button', { 'button_disabled': disabled, 'button_loading': loading, }, { 'button_size_xl': size === 'xl', 'button_size_l': size === 'l', }, { 'button_primary': color === 'primary', 'button_secondary': color === 'secondary', } ]"> {{ text }} </button></template>
Разница в голове. Вы сразу видите:
-
Это блок
.button. -
У него есть состояния (
_disabled,_loading). -
Есть варианты размера и цвета.
-
Ничего не переплетается. Каждый класс — одна задача.
А CSS при этом становится предсказуемым:
.button { /* базовые стили */ ...}.button_disabled { opacity: 0.5; ...}.button_loading { /* стили состояния загрузки */}.button_primary:not(.button_disabled, .button_loading):hover { background-color: #0055ff;}
Обратите внимание на :not(). Это не требование БЭМ, а приятный бонус. Вы не плодите лишние классы вроде .button_primary_not_disabled. CSS сам фильтрует состояния. Чисто, масштабируемо, легко читать.
Когда БЭМ не нужен?
Если вы пишете лендинг на 2 экрана или прототип на выходные — не мучайте себя. Tailwind или обычные классы справятся быстрее. БЭМ раскрывается там, где есть команда, долгая поддержка и сложная UI-система.
Какой же итог?
Vue отвечает за логику и реактивность. БЭМ отвечает за предсказуемость стилей. Вместе они дают компонент, который не ломается при масштабировании, не пугает новых разработчиков и не превращается в технический долг через полгода.
Не нужно фанатизма. Не нужно слепого следования правилам. Нужно понимание, зачем вы пишете каждый класс. БЭМ просто напоминает об этом.
Попробуйте в следующем компоненте. Если через неделю не станет легче — вернитесь к тому, как было. Но скорее всего, вы уже не захотите.
ссылка на оригинал статьи https://habr.com/ru/articles/1022210/