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(块)

独立的、有意义的组件或模块。比如:menucardbuttonform

▶ 示例

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>
逻辑代码 41 行(超过 40 行限制,仅展示)

对应的 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 的优势

  1. 唯一性:每个类名在整个项目中都是唯一的,不会冲突
  2. 自解释:看到类名就知道是什么组件的什么部分
  3. 层级清晰:块 → 元素 → 修饰符的关系一目了然
  4. 可维护:修改一个块时不用担心影响其他块
  5. 无嵌套:不需要深层嵌套 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 的价值就体现出来了。

📖 小节

📝 作业

  1. 使用 BEM 命名规范创建一个"用户评论"组件,包含头像、用户名、时间、评论内容和点赞按钮。
  2. 将下面的 CSS 改写成 BEM 风格:
CSS
.profile { ... }
.profile .photo { ... }
.profile .info { ... }
.profile .info .name { ... }
.profile .info .desc { ... }
.profile .actions { ... }
.profile .actions .btn { ... }
.profile .actions .btn:hover { ... }
  1. 思考在你的项目中引入 BEM 命名规范,选择一个页面用小模块实践一下。
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏