跳到主要内容

BEM 命名规范实战:用 Block__Element--Modifier 终结 CSS 类名混乱

BEM 把类名拆成块、元素、修饰符三段,双下划线接元素,双连字符接修饰符。本文讲清这套 CSS 命名规范如何避开嵌套地狱和样式冲突,附真实命名例子。

发布于 作者 李雷
#BEM #CSS命名规范 #前端工程化 #命名约定

BEM 命名规范实战:用 Block__Element--Modifier 终结 CSS 类名混乱

我接手过一个跑了三年的后台项目,翻开它的样式表,.title 有 11 处定义,.active 有 23 处,改一个按钮颜色能连带把侧边栏弄花。问题不在写样式的人不努力,而在于没有一套让类名"看一眼就知道归谁管"的约定。BEM 就是为这件事来的。

BEM 到底是什么

BEM 是 Block、Element、Modifier 三个词的缩写,来自 Yandex 团队的工程实践。它把一个组件的类名拆成三段:

  • 块(Block):独立的组件,比如 cardmenusearch
  • 元素(Element):块里离开块就没意义的部分,用双下划线接上,比如 card__title
  • 修饰符(Modifier):表示外观或状态的标记,用双连字符接上,比如 card--featured

核心约定就一条:block__element--modifier。元素前是两个下划线,修饰符前是两个连字符。一个完整的状态类长这样:menu__item--active

为什么是双下划线和双连字符

很多人第一次见会嫌它丑,但这个设计是有道理的。单个连字符要留给每一段内部的 kebab-case 命名。

举个真实例子:一个搜索表单块叫 search-form,它自己的两个单词之间就得用连字符。这时元素边界如果也用单连字符,search-form-input 就完全分不清哪段是块名、哪段是元素名。BEM 用双下划线把它拆成 search-form__input,再加状态读作 search-form__input--focused,边界一目了然。

同样,修饰符用双连字符而不是单个。card--active 是 card 块的 active 状态;card-active 会被读成一个叫 card-active 的全新块。一个字符之差,语义完全不同。

一个卡片组件的完整命名

假设你要写一张文章卡片,它有标题、封面图、页脚,还有一个"精选"变体和标题"加大"的样式。规范命名应该是:

card                  /* 块 */
card__title           /* 元素:标题 */
card__cover           /* 元素:封面 */
card__footer          /* 元素:页脚 */
card--featured        /* 修饰符:精选卡片 */
card__title--large    /* 元素上的修饰符:大标题 */

写进 HTML 是这样:class="card card--featured",标题那行是 class="card__title card__title--large",基础类和变体一起挂上去。

对应的 SCSS 用 & 父选择器组织,源码读起来还是嵌套的,编译出来却是扁平选择器:

.card {
  &__title {
    &--large { font-size: 1.5rem; }
  }
  &--featured { border: 2px solid gold; }
}

BEM 怎么避开嵌套地狱

传统写法很容易滑向 .sidebar .panel .list li a.active 这种长选择器,层层嵌套,优先级越堆越高,想覆盖一个样式就得再叠一层,最后谁也不敢删。

BEM 的每个类都是单层选择器,.card__title--large 的优先级和 .btn 完全一样。没有后代选择器,就没有优先级军备竞赛,改样式不用靠 !important 强压。这就是团队总把 BEM 和 Sass 搭在一起的原因:源码靠 & 嵌套保持可读,产物靠扁平类保持可维护。

有三个坑特别常见,值得记下:元素不要嵌元素,card__body__title 是错的,应该永远扁平地写成 card__title;修饰符别用单连字符;也别让修饰符脱离它的块单独存在,一个全局裸 .active 会渗进所有无关组件。

手敲容易错,交给工具

道理都懂,真到一行行敲的时候,少打一个下划线、casing 写串了,选择器就悄悄失效,还不报错。我自己带新人时发现,光讲约定记不住,让他打开 BEM 类名生成器,填进 cardtitleactive,看着 card__title--active 连同 SCSS 一起冒出来,规则当场就通了。

工具会把每一段都规范化成 kebab-case:cardTitleCard TitleCARD_TITLE 都落到同一个 card-title,团队再也不会因为大小写在评审里吵。如果你只是想统一某一批字符串的写法,大小写转换工具 也能顺手处理。配合起来,从命名约定到落地样式表,整条路就顺了。


Made by Toolora · Updated 2026-06-13