BEM命名能立刻看懂样式归属,因其类名自带模块、元素、状态三层上下文,如header__logo--dark明确表示属于header模块的logo元素且处于dark状态,避免依赖HTML或JS推断作用域。

为什么BEM命名能立刻看懂样式归属
因为类名自带模块、元素、状态三层上下文,不用翻HTML或JS就能判断样式作用域。header__logo--dark 比 logo-dark 或 dark-logo 明确得多:它属于 header 模块下的 logo 元素,且处于 dark 状态。
常见错误是把 BEM 当成“加下划线的命名习惯”,结果写成 user-info__avatar-small——small 是尺寸,不是状态,应该用修饰符(modifier)语义,比如 user-info__avatar--sized-small 或直接用 user-info__avatar--compact。
- 模块名必须是独立功能单元,如
card、modal、form-field,不能是泛义词如section、block - 元素名用双下划线
__连接,必须依附于模块存在,不单独使用(即不写__title) - 修饰符用双中划线
--,只描述状态或变体(--disabled、--expanded),不表达层级或位置
BEM在CSS预处理器里的实际写法差异
用 Sass 写 BEM 时,嵌套容易掩盖真实类名结构,导致生成冗余选择器或误判作用域。比如:
.card {
&__header { … }
&__body { … }
&--featured { … }
}
这看着干净,但编译后是 .card__header、.card__body、.card--featured,没问题;可一旦加了 &.is-open 这种条件嵌套,就可能产出 .card.is-open__header 这种非法选择器。
立即学习“前端免费学习笔记(深入)”;
- Sass 中避免用
&嵌套修饰符到元素上,修饰符应始终作用于模块级类名 - PostCSS 插件如
postcss-bem能强制校验命名格式,但会增加构建步骤,小项目不如人工约定+ESLint插件stylelint-selector-bem-pattern实用 - 如果用 CSS-in-JS(如 Emotion),推荐用
css函数配合 BEM 字符串模板,而非动态拼接类名,否则破坏静态分析能力
遇到 legacy CSS 时怎么渐进式引入BEM
老项目里满屏 .left、.clear、.red-text,硬推 BEM 会卡住。关键是先隔离,再迁移,不追求一步重命名。
- 新组件一律用 BEM,旧组件只在修改样式逻辑时顺手改掉对应类名(例如修一个
.btn的 hover 行为,就顺便把它和它的子元素改成button/button__icon) - 禁止新增
!important或 ID 选择器来覆盖老样式,改用更高优先级的 BEM 组合,比如从.sidebar .item升级为.sidebar__item--active - 用浏览器 DevTools 的 “Computed” 面板反查哪些类名已无实际作用,批量清理——别信“以后再删”的承诺,三个月后更不敢动
为什么团队协作中BEM比CSS Modules更易对齐
CSS Modules 解决了全局污染,但类名还是靠人写,styles.title 和 styles.headerText 在不同文件里可能指同一视觉元素,Review 时很难发现重复实现。BEM 把命名规则变成接口契约:card__title 必须出现在所有 card 相关组件中,且含义一致。
- 设计师给的标注稿里写“主卡片标题字号 18px”,前端立刻知道该改
card__title,而不是猜CardTitle、cardTitle、title-text哪个是它 - 搜索
card__title能同时定位 HTML、CSS、JS(事件委托监听)、测试代码,而 CSS Modules 的styles.title在 JS 里无法被文本搜索直接命中 - 注意:BEM 不解决样式复用问题,
button--primary和card__action--primary仍可能重复定义颜色边框,这时需抽离theme变量,BEM 和 CSS 自定义属性可以共存
__ 和 --,而是每次写新类名时多问一句:“这个类脱离当前 HTML 结构,别人还能不能猜出它在哪、干啥、能不能复用”。


















