bem 是为解决 css 作用域混乱、组件复用困难、团队协作样式冲突而设计的命名约束;其核心是 block(独立功能单元)、element(block 内子节点)、modifier(状态/外观变体)三者职责分明的结构化约定。

直接说结论:BEM 不是“让类名变长的规范”,而是为了解决 CSS 作用域混乱、组件复用困难、团队协作时样式冲突这三类真实问题而设计的命名约束。它本身不改变 CSS 行为,但能极大降低 !important、嵌套过深、覆盖调试耗时等高频痛点。
什么是 BEM 的三个字母对应的真实含义
BEM 是 Block(块)、Element(元素)、Modifier(修饰符)的缩写,但它不是抽象概念,而是对 HTML 结构和样式职责的明确切分:
-
Block:独立、可复用的功能单元,比如header、card、button;它不依赖上下文,自身有完整语义和样式边界 -
Element:仅在某个Block内部有意义的子节点,比如card__title、card__content;它永远不能脱离所属Block单独存在 -
Modifier:描述Block或Element的状态或外观变体,比如button--primary、card--compact;它不新增结构,只叠加样式
常见错误是把 user-avatar__img 当作合法 Element —— 实际上如果 user-avatar 本身没定义为 Block(比如没封装成独立组件、没统一控制尺寸/圆角),这个命名就失去意义,只是徒增冗余。
怎么写才不算“假 BEM”:避开四个典型误用
很多项目号称用了 BEM,但实际仍难维护,问题出在命名与结构脱节。以下写法看似合规,实则违背 BEM 设计初衷:
- 嵌套层级欺骗:
header__nav__item❌ ——nav应该是独立Block,不是header的 Element;正确是nav__item+ 在 HTML 中组合使用 - Modifier 滥用布尔值:
button--is-loading❌ —— BEM Modifier 应表达“是什么”,而非“是不是”;应写为button--loading,靠 JS 控制 class 存在与否 - Element 跨 Block 复用:
list__item被同时用于menu和search-result❌ —— 这说明list__item其实是通用原子类,应归入基础工具层(如u-flex),不属于 BEM 范畴 - 用连字符代替双下划线:
card-title替代card__title❌ —— 破坏 BEM 解析一致性,导致无法用 PostCSS 插件自动校验或生成作用域
如何配合现代前端工程落地 BEM
BEM 的价值在工程中放大,取决于是否与构建流程、组件边界、团队约定对齐:
- React/Vue 组件名与 Block 名保持一致:
Card.jsx→ 对应类名前缀card;避免出现CardComponent.scss却用card-comp__body - 用
postcss-bem或stylelint-selector-bem-pattern强制校验,禁止出现.card .title这类后代选择器 —— BEM 原则上只允许单 class 选择器 - Modifier 值建议限定为枚举:
button--size-small/--medium/--large,避免button--size-24px这种不可收敛的写法 - 当需要主题切换(如 dark mode),不要写
card--dark,而应将主题作为顶层Block隔离:theme-dark card,否则 Modifier 层级会爆炸
最易被忽略的一点:BEM 不解决 CSS 优先级问题,它只是让优先级变得可预测。如果你还在靠增加选择器长度(如 .header .nav .nav__item)来提权,说明 Block 边界没划清,得回退一步重审组件拆分逻辑。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











