介绍
BEM:Block(块)‑Element(元素)‑Modifier(修饰符),是一套组件化 CSS 命名约定,解决类名冲突、样式耦合、语义不清晰问题,适合大型项目。
一、概念
1. Block 块
独立、可复用的组件块,代表一个整体模块,不依赖其他组件。
- 命名:使用小写字母,连字符
-连接多单词 - 特征:可以独立使用
示例:
/* 块:卡片、按钮、导航栏 */
.card {}
.nav-bar {}
.search-form {}
✅正确:.user-card
❌错误:.UserCard(不要驼峰)、.userCard(不要小驼峰)
2. Element 元素
块内部的子部分,不能脱离块单独使用。
- 语法:
block__element,使用两个下划线__分隔块和元素 - 含义:属于某个块的子元素,脱离块就没有意义
.card__title {} /* card块里的标题 */
.card__content {} /* card块里的内容 */
.card__btn {} /* card块里的按钮 */
.nav-bar__item {} /* 导航栏的菜单项 */
不允许多层嵌套
block__elem1__elem2!
❌错误:.card__header__title
✅正确:.card__header-title(多单词用单横线,只用一次__)
3. Modifier 修饰符
用来描述块/元素的状态、变体、外观(尺寸、颜色、激活、禁用、选中)。
- 语法:
block--modifier或者block__element--modifier,两个短横线-- - 修饰符本身不单独存在,必须依附块或元素
/* 块的修饰符 */
.card--dark {} /*深色版本卡片*/
.btn--primary {} /*主按钮*/
.btn--disabled {} /*禁用状态按钮*/
/*元素的修饰符*/
.nav-bar__item--active {} /*导航激活项*/
.card__btn--large {} /*卡片内大按钮*/
二、语法
块:block-name
元素:block-name__element-name
修饰块:block-name--modifier-name
修饰元素:block-name__element-name--modifier-name
HTML示例完整演示
<!-- BEM示例:卡片组件 -->
<div class="card card--dark">
<div class="card__header">
<h3 class="card__title">标题</h3>
</div>
<div class="card__content">内容文本</div>
<button class="card__btn card__btn--primary">确定</button>
</div>
/*块*/
.card {
border:1px solid #eee;
}
/*块修饰符*/
.card--dark {
background:#222;
color:#fff;
}
/*元素*/
.card__header {
padding:10px;
}
.card__title {
font-size:16px;
}
.card__btn {
padding:6px 12px;
}
/*元素修饰符*/
.card__btn--primary {
background:#1677ff;
color:#fff;
}
关键点:修饰符类永远和基础类同时写在标签上,不要单独写修饰符类。
❌ <div class="card--dark">
✅ <div class="card card--dark">
三、命名多单词规则
- 块、元素、修饰符内部多个单词:用单横线
- - 块与元素分隔:双下划线
__ - (块|元素)与修饰符分隔:双横线
--
示例:
.user-profile 块:用户资料
.user-profile__avatar 元素:用户资料的头像
.user-profile__avatar--large 修饰:大尺寸头像
四、BEM常见误区
1. ❌不要无限嵌套__
//错误
.card__header__title{}
//正确
.card__header-title{}
2. ❌修饰符不能单独使用
修饰符只是追加样式,基础类必须保留。
3. ❌不要把页面结构当块
块是可复用组件,不是页面布局容器;页面布局可以单独写布局类。
4. ❌不要 DOM 结构和 BEM 强行绑定
BEM是组件语义,不是html层级;DOM嵌套深不代表要多加__。
5. ✅允许块嵌套另一个块
一个块内部可以放另一个独立块:
<div class="card">
<div class="search-form"></div> <!-- search-form是独立块,不用写card__search-form -->
</div>
如果这个search-form只是card内部专属、不能单独拿出去用,才写成card__search-form。
五、BEM的优缺点
✅优点
- 语义化:看类名就知道属于哪个组件,是什么角色,什么状态
- 避免选择器冲突,减少权重滥用,少用
!important - 组件化,方便复制、迁移、复用组件
- 便于团队协作,统一命名习惯,大项目维护友好
- 和css module、scss配合非常适配
❌缺点
- 类名很长,html标签会比较臃肿
- 新手容易滥用
__,过度嵌套命名 - 需要严格遵守约定,团队要统一认知
六、DOM多层嵌套的写法
HTML嵌套,跟BEM的__没关系!DOM嵌套 ≠ BEM嵌套,__只能出现一次,不能写 a__b__c。
场景举例:DOM是三层嵌套
<div class="card"> <!-- block:card -->
<div class="card__head"> <!-- element -->
<div>
<h2>标题</h2> <!-- DOM深层嵌套 -->
</div>
</div>
</div>
❌错误写法(跟着DOM层级多加__)
.card__head__title {} /*禁止双下划线叠加*/
✅正确:元素内部多单词用单横线 -
<div class="card">
<div class="card__head">
<div>
<h2 class="card__head-title">标题</h2>
</div>
</div>
</div>
.card {}
.card__head {}
.card__head-title {} /* 一次__,后面用单横线 */
规则:block__xxx-yyy
__:仅1次,分割块和第一级子元素- 后面再细分,全部用普通
-,不要再加__
案例2:更深DOM层级例子
HTML DOM结构(4层标签嵌套)
<div class="modal">
<div class="modal__wrapper">
<div class="modal__wrapper-inner">
<div class="modal__wrapper-inner-content">
<h3 class="modal__wrapper-inner-title">弹窗标题</h3>
</div>
</div>
</div>
</div>
CSS:
/* block */
.modal {}
/* element,__只写一次,后面全部单横线 */
.modal__wrapper {}
.modal__wrapper-inner {}
.modal__wrapper-inner-content {}
.modal__wrapper-inner-title {}
注意: modal__wrapper-inner-title 不要写成 modal__wrapper-inner-content-title,不需要把完整 DOM 路径全部拼接进类名。类名超长,DOM 改一层,类名就要大改,维护灾难。
两种嵌套区分:「组件内部子元素」vs「嵌入另一个独立组件」
情况A:这个子模块只属于父块,拿出去不能用 → 用 block__xxx-yyy
<div class="card">
<div class="card__footer">
<button class="card__footer-btn">按钮</button>
</div>
</div>
.card {}
.card__footer {}
.card__footer-btn {}
情况B:里面是完全独立组件,可以拿出来到处复用 → 不要再加父块前缀,直接用自己的block
❌不要写成 .card__button
✅直接写 .button
<div class="card">
<div class="card__footer">
<button class="button button--primary">确认</button>
</div>
</div>
.card {}
.card__footer {}
.button {}
.button--primary {}
❌禁止操作汇总
禁止多个
__/* 错 */ .modal__a__b__c {} /* 对 */ .modal__a-b-c {}不要用后代选择器去规避BEM(很多人图省事这么写,BEM不推荐)
/* 不推荐,增加权重,破坏BEM扁平思想 */ .modal .modal__wrapper .title {} /* 推荐:直接给标签挂类名 */ .modal__wrapper-title {}
BEM思想:尽量给需要样式的DOM节点直接挂class,少依赖DOM层级关系选择样式,就算HTML嵌套很深,每个需要样式的节点直接写完整BEM类名。
七、好处
解决大型项目 CSS 混乱、选择器权重打架、命名冲突、组件不好复用的问题。
- 类名自带语义,看 class 就知道是什么
- 彻底减少样式冲突
- CSS 选择器扁平,权重可控,少用
!important - 组件可复制、迁移复用
- HTML 结构改变,CSS 不容易坏
- 团队协作统一,降低沟通成本
- 和 CSS Module / Vue scoped / Tailwind 之外的传统项目适配很好
总结
- 块内单词单横杠,块元之间双下划;
- 状态变体双横杠,修饰永远不独行;
- 下划线只准一次,独立块不要套进元素。
- HTML随便嵌套几层,BEM的
__最多出现一次; - 后续细分全部
-; - 内部如果是独立组件,直接用自己的块,不要加父块前缀。