CSS: BEM 与样式规范
随着项目规模的增长,CSS 的维护难度会越来越大。如果没有良好的命名规范,很容易出现样式冲突、覆盖混乱、不敢改代码的情况。BEM 就是一种解决这些问题的命名规范。
1. 为什么要用命名规范?
想象一下,你的项目中有几十个页面、几百个组件,如果都随意命名:
▶ 示例
HTML
<style>
/* 混乱的命名 */
.box {
background: #fce4ec;
border: 1px solid #f48fb1;
padding: 10px;
margin-bottom: 8px;
}
.header .box {
background: #e3f2fd;
border: 1px solid #90caf9;
padding: 10px;
}
.content .box .title {
background: #fff3e0;
border: 1px solid #ffb74d;
padding: 10px;
}
</style>
<header class="header">
<div class="box">Header 区域的 box</div>
</header>
<div class="content">
<p>主要内容区域</p>
<div class="box">
Content 区域的 box
<div class="title">标题文字</div>
</div>
</div>
问题来了——box 到底是什么?改了 .box 会影响哪里?有没有和其他地方的 .box 冲突?
命名规范就是为了解决这些问题。
2. BEM 是什么?
BEM 代表 Block(块)、Element(元素)、Modifier(修饰符),是一种 CSS 命名方法论。
(1) Block(块)
独立的、有意义的组件或模块。比如:menu、card、button、form。
▶ 示例
HTML
<style>
.menu { ... }
.card { ... }
.button { ... }
</style>
<div class="menu">导航菜单项</div>
<div class="bem-item�">BEM 演示项目</div>
<div class="card">Card</div>
<button class="button">Button</button>
(2) Element(元素)
块的组成部分,用双下划线 __ 连接。
▶ 示例
HTML
<style>
.menu__item { ... } /* menu 中的 item */
.card__title { ... } /* card 的标题 */
.card__image { ... } /* card 的图片 */
.card__content { ... } /* card 的内容 */
.button__icon { ... } /* button 的图标 */
</style>
<div class="menu__item">Menu__item</div>
<div class="bem-item�">BEM 演示项目</div>
<div class="card__title">Card__title</div>
<div class="card__image">Card__image</div>
<div class="card__content">Card__content</div>
<div class="button__icon">Button__icon</div>
(3) Modifier(修饰符)
块或元素的不同状态,用双连字符 -- 连接。
▶ 示例
HTML
<style>
.button--primary { ... } /* 主要按钮 */
.button--large { ... } /* 大按钮 */
.button--disabled { ... } /* 禁用按钮 */
.menu__item--active { ... } /* 活跃的菜单项 */
.card--featured { ... } /* 特色卡片 */
</style>
<button class="button--primary">Button primary</button>
<div class="bem-item�">BEM 演示项目</div>
<button class="button--large">Button large</button>
<button class="button--disabled">Button disabled</button>
<div class="menu__item--active">Menu__item active</div>
<div class="card--featured">Card featured</div>
(4) 完整的 BEM 示例
▶ 示例
HTML
📖 仅展示
<style>
/* Block */
.card {
border: 1px solid #eee;
border-radius: 8px;
overflow: hidden;
}
/* Element */
.card__image {
width: 100%;
height: 200px;
object-fit: cover;
}
.card__title {
font-size: 1.25rem;
margin: 0;
padding: 16px 16px 0;
}
.card__content {
padding: 8px 16px 16px;
color: #666;
}
.card__footer {
padding: 12px 16px;
border-top: 1px solid #eee;
}
/* Modifier */
.card--featured {
border-color: #3498db;
box-shadow: 0 4px 12px rgba(52, 152, 219, 0.3);
}
.card__title--large {
font-size: 1.5rem;
}
</style>
<div class="card">Card</div>
<span class="border-demo">实线边框</span>
<div class="card__image">Card__image</div>
<div class="card__title">Card__title</div>
<div class="card__content">Card__content</div>
<div class="card__footer">Card__footer</div>
<span class="border-demo">实线边框</span>
<div class="card--featured">Card featured</div>
<div class="card__title--large">Card__title large</div>
对应的 HTML:
HTML
<div class="card card--featured">
<img class="card__image" src="/assets/images/tutorials/html-example-photo.webp" alt="Sample image">
<h2 class="card__title card__title--large">Card__title card__title large</h2>
<div class="card__content">Card__content</div>
<div class="card__footer">Card__footer</div>
</div>
3. BEM 的优势
- 唯一性:每个类名在整个项目中都是唯一的,不会冲突
- 自解释:看到类名就知道是什么组件的什么部分
- 层级清晰:块 → 元素 → 修饰符的关系一目了然
- 可维护:修改一个块时不用担心影响其他块
- 无嵌套:不需要深层嵌套 CSS,性能更好
4. 避免深层嵌套
使用 BEM 后,你不再需要深层嵌套的选择器:
▶ 示例
HTML
<style>
/* ❌ 不使用 BEM */
.card { ... }
.card .header { ... }
.card .header .title { ... }
.card .content .body .text { ... }
/* ✅ 使用 BEM */
.card { ... }
.card__header { ... }
.card__title { ... }
.card__text { ... }
</style>
<div class="card">Card</div>
<div class="bem-item�">BEM 演示项目</div>
<div class="card">Card</div>
<header class="header">Header</header>
<div class="card">Card</div>
<header class="header">Header</header>
<div class="title">标题文字示例</div>
<div class="card">Card</div>
<div class="content">主要内容区域</div>
<div class="body">正文内容区域</div>
<div class="text">普通正文文本内容示例</div>
<div class="card__header">Card__header</div>
<div class="card__title">Card__title</div>
<div class="card__text">Card__text</div>
5. 其他命名规范
✅ 团队选择: BEM 是目前最流行的方案,但不是唯一的。如果你的团队觉得 BEM 类名太长,可以考虑用更简洁的命名方案(如 SUIT CSS、Atomic CSS)。关键是团队统一——不管用什么方案,全站保持一致。
(1) OOCSS(面向对象 CSS)
将视觉样式从结构中分离,强调"复用":
▶ 示例
HTML
<style>
/* 结构 */
.media { ... }
.media__img { ... }
.media__body { ... }
/* 皮肤 */
.skin-blue { background: blue; }
.skin-red { background: red; }
</style>
<div class="media">媒体内容区域</div>
<div class="bem-item�">BEM 演示项目</div>
<div class="media__img">媒体区域的图片</div>
<div class="media__body">媒体区域的描述文字</div>
<div class="skin-blue">蓝色主题皮肤</div>
<div class="skin-red">红色主题皮肤</div>
(2) SMACSS
将 CSS 分为五类:Base、Layout、Module、State、Theme。用不同前缀区分:
▶ 示例
HTML
<style>
/* Layout: l- */
.l-header { ... }
.l-sidebar { ... }
/* Module: 模块名 */
.nav { ... }
.nav-item { ... }
/* State: is- */
.is-active { ... }
.is-hidden { ... }
</style>
<div class="l-header">L header</div>
<div class="bem-item�">BEM 演示项目</div>
<div class="l-sidebar">L sidebar</div>
<nav class="nav">Nav</nav>
<nav class="nav-item">Nav item</nav>
<div class="is-active">激活状态元素</div>
<div class="is-hidden">隐藏状态元素</div>
(3) 原子类(Functional CSS)
前面已经学过,每个类只做一件事:
CSS
.flex { display: flex; }
.text-center { text-align: center; }
Tailwind CSS 是目前最流行的原子类框架。
6. 选择哪种规范?
| 规范 | 适合场景 |
|---|---|
| BEM | 传统项目、需要清晰命名 |
| 原子类 | 快速开发、团队熟练 |
| SMACSS | 大型项目、需分类管理 |
| 混用 | BEM + 原子类很常见 |
7. CSS 风格指南
除了命名规范,还有一些代码书写的建议:
▶ 示例
HTML
<style>
/* 1. 使用简写属性 */
/* ❌ */
.card {
margin-top: 10px;
margin-right: 20px;
margin-bottom: 10px;
margin-left: 20px;
}
/* ✅ */
.card {
margin: 10px 20px;
}
/* 2. 属性按类型分组 */
.card {
/* 定位 */
position: relative;
z-index: 1;
/* 布局 */
display: flex;
gap: 10px;
/* 尺寸 */
width: 100%;
min-height: 200px;
/* 间距 */
padding: 20px;
margin: 10px 0;
/* 视觉 */
background: #fff;
border: 1px solid #eee;
border-radius: 8px;
}
</style>
<div class="card">Card</div>
<span class="border-demo">实线边框</span>
❓ 常见问题
Q BEM 类名太长怎么办?
A 类名长是 BEM 的设计取舍——宁可名字长一点,也要避免样式冲突。如果觉得太长,可以考虑用更短的命名(如
card__title 简写为 card-t),但建议全项目统一风格。Sass 的嵌套语法可以大幅减少重复书写。Q BEM 中的
-- 和 __ 可以混用吗?A 可以。
block__element--modifier 是标准写法,表示"某个块下的某个元素的某个状态"。比如 card__title--highlight 表示"卡片的标题的高亮状态"。注意不要反过来写成 block--modifier__element,顺序很重要。Q BEM 适合小项目吗?
A 对于只有几个页面的小项目,BEM 确实有点杀鸡用牛刀的感觉。直接用简单的 class 命名(如
.title、.btn)就够了。但当项目超过 10 个页面或有多个开发者协作时,BEM 的价值就体现出来了。📖 小节
- BEM 是最好的 CSS 命名规范之一
- 它通过 Block(块)、Element(元素)、Modifier(修饰符)的分层结构,让 CSS 类名变得自解释、无冲突、易维护
- BEM 不要求嵌套选择器,每个类名都是独立的,这有效避免了选择器优先级混乱的问题
- 无论你最终选择哪种规范,保持团队一致才是最重要的
📝 作业
- 使用 BEM 命名规范创建一个"用户评论"组件,包含头像、用户名、时间、评论内容和点赞按钮。
- 将下面的 CSS 改写成 BEM 风格:
CSS
.profile { ... }
.profile .photo { ... }
.profile .info { ... }
.profile .info .name { ... }
.profile .info .desc { ... }
.profile .actions { ... }
.profile .actions .btn { ... }
.profile .actions .btn:hover { ... }
- 思考在你的项目中引入 BEM 命名规范,选择一个页面用小模块实践一下。