您现在的位置是:网站首页 > BEM命名方法论文章详情

BEM命名方法论

BEM命名方法论的核心思想

BEM(Block Element Modifier)是一种前端CSS命名方法论,由Yandex团队提出。它通过严格的命名约定来解决CSS中的命名冲突和样式污染问题。BEM将界面拆分为独立的块(Block),块中包含元素(Element),并通过修饰符(Modifier)来描述它们的状态或变体。

/* 传统命名方式 */
.user-profile {
  padding: 20px;
}
.user-profile img {
  width: 100px;
}
.user-profile.active {
  background: blue;
}

/* BEM命名方式 */
.user-profile {
  padding: 20px;
}
.user-profile__avatar {
  width: 100px;
}
.user-profile--active {
  background: blue;
}

块(Block)的命名规则

块是BEM中的独立实体,代表一个自包含的组件或模块。块的命名应该简洁明了,使用单个单词或连字符连接的多个单词。

/* 好的块命名 */
.menu {}
.search-form {}
.user-card {}

/* 不好的块命名 */
.navMenu {} /* 使用驼峰 */
.search_form {} /* 使用下划线 */
.userCardContainer {} /* 过于复杂 */

块在HTML中的使用示例:

<div class="search-form">
  <!-- 块内容 -->
</div>

元素(Element)的命名规则

元素是块的组成部分,不能独立于块存在。元素名称通过双下划线__与块名称连接。

.search-form__input {}
.search-form__button {}
.menu__item {}

元素在HTML中的结构:

<form class="search-form">
  <input class="search-form__input" type="text">
  <button class="search-form__button">Search</button>
</form>

元素命名的关键点:

  1. 元素名称必须与块名称通过双下划线连接
  2. 不能单独使用元素类名(如.search-form__input不能脱离.search-form使用)
  3. 元素不能嵌套命名(避免.block__elem1__elem2这种形式)

修饰符(Modifier)的使用方法

修饰符定义块或元素的外观、状态或行为变化。修饰符通过双连字符--与块或元素名称连接。

/* 块修饰符 */
.menu--vertical {
  display: flex;
  flex-direction: column;
}

/* 元素修饰符 */
.button--disabled {
  opacity: 0.5;
  pointer-events: none;
}

修饰符在HTML中的使用:

<nav class="menu menu--vertical">
  <a href="#" class="menu__item menu__item--active">Home</a>
  <a href="#" class="menu__item">About</a>
</nav>

<button class="button button--disabled">Submit</button>

修饰符的两种使用方式:

  1. 单独使用修饰符类(需要基础类)
<div class="block block--modifier"></div>
  1. 简化方式(某些BEM变体)
<div class="block--modifier"></div>

BEM的实际应用案例

一个完整的卡片组件实现:

/* 块定义 */
.card {
  border: 1px solid #ddd;
  border-radius: 4px;
  overflow: hidden;
  max-width: 300px;
}

/* 元素定义 */
.card__image {
  width: 100%;
  height: auto;
  display: block;
}

.card__content {
  padding: 15px;
}

.card__title {
  margin: 0 0 10px;
  font-size: 1.2em;
}

.card__description {
  color: #666;
  margin-bottom: 15px;
}

.card__button {
  display: inline-block;
  padding: 8px 15px;
  background: #3498db;
  color: white;
  text-decoration: none;
  border-radius: 3px;
}

/* 修饰符定义 */
.card--featured {
  border-color: #3498db;
  box-shadow: 0 0 10px rgba(52, 152, 219, 0.5);
}

.card__button--secondary {
  background: #2ecc71;
}

.card__title--large {
  font-size: 1.5em;
}

对应的HTML结构:

<div class="card card--featured">
  <img src="image.jpg" class="card__image">
  <div class="card__content">
    <h3 class="card__title card__title--large">Featured Product</h3>
    <p class="card__description">This is a featured product with special styling.</p>
    <a href="#" class="card__button card__button--secondary">Learn More</a>
  </div>
</div>

BEM在预处理器中的使用

在Sass/SCSS中,BEM可以借助嵌套语法更清晰地组织代码:

.card {
  border: 1px solid #ddd;
  
  &--featured {
    border-color: #3498db;
  }
  
  &__image {
    width: 100%;
    
    &--rounded {
      border-radius: 50%;
    }
  }
  
  &__button {
    padding: 8px 15px;
    
    &--secondary {
      background: #2ecc71;
    }
  }
}

Less中的BEM实现:

.menu {
  display: flex;
  
  &--vertical {
    flex-direction: column;
  }
  
  &__item {
    padding: 10px;
    
    &--active {
      font-weight: bold;
    }
  }
}

BEM命名中的常见问题

  1. 过度嵌套问题 错误示例:
.nav__list__item__link {} /* 避免这种深度嵌套 */

正确做法:

.nav__item-link {} /* 简化元素命名 */
  1. 修饰符滥用问题 错误示例:
.btn--red {}
.btn--blue {}
.btn--green {}

更好的方式:

.btn--primary {}
.btn--secondary {}
  1. 全局样式与BEM的冲突
/* 全局样式 */
a {
  color: #3498db;
}

/* BEM组件中的链接需要更具体 */
.menu__link {
  color: #333; /* 这会覆盖全局样式 */
}

BEM与其他方法论结合

BEM可以与SMACSS、OOCSS等方法论结合使用:

/* SMACSS的布局规则 + BEM */
.l-header {
  /* 布局样式 */
}

/* BEM组件 */
.site-nav {
  /* 组件样式 */
}

.site-nav__item {
  /* 组件元素样式 */
}

与CSS Modules结合时,BEM命名可以简化:

/* 使用CSS Modules时 */
.item {
  composes: common from './shared.css';
}

.active {
  /* 修饰符样式 */
}

BEM在大型项目中的优势

  1. 样式隔离:每个组件都有独立命名空间
/* 不同组件的button不会冲突 */
.search-form__button {}
.user-card__button {}
  1. 可维护性:清晰的命名让代码更易理解
/* 一看就知道这是导航菜单的激活状态项 */
.nav-menu__item--active {}
  1. 团队协作:统一的命名规范减少沟通成本
/* 所有开发人员都遵循相同规则 */
.article-card {}
.article-card__header {}
.article-card--featured {}

BEM的变体和替代方案

  1. ABEM:适配BEM(Adapted BEM)
.o-block-name {}
.o-block-name__element-name {}
.o-block-name--modifier-name {}
  1. BEMIT:BEM + ITCSS
.c-btn {} /* 组件 */
.c-btn--primary {} /* 修饰符 */
.u-mt-20 {} /* 工具类 */
  1. SUIT CSS
.ComponentName {}
.ComponentName--modifierName {}
.ComponentName-descendantName {}
.ComponentName.is-stateOfComponent {}

BEM命名中的实用技巧

  1. 处理多个修饰符
<button class="btn btn--primary btn--large btn--rounded">
  1. 状态类与修饰符
/* 使用is-前缀表示JS控制的状态 */
.tabs__panel.is-active {
  display: block;
}
  1. 工具类与BEM结合
/* 工具类 */
.u-text-center {
  text-align: center;
}

/* BEM组件 */
.alert {
  /* 组件样式 */
}

/* 在HTML中使用 */
<div class="alert u-text-center">

BEM在React/Vue组件中的应用

React组件中的BEM实现:

function Button({ primary, children }) {
  const className = `button ${primary ? 'button--primary' : ''}`;
  return <button className={className}>{children}</button>;
}

// 使用
<Button primary>Submit</Button>

Vue单文件组件中的BEM:

<template>
  <div class="user-card" :class="{ 'user-card--inactive': !isActive }">
    <img class="user-card__avatar" :src="avatarUrl">
    <h3 class="user-card__name">{{ userName }}</h3>
  </div>
</template>

<style scoped>
.user-card {
  /* 块样式 */
}
.user-card--inactive {
  /* 修饰符样式 */
}
.user-card__avatar {
  /* 元素样式 */
}
</style>

BEM命名的性能考量

  1. 选择器效率:BEM命名通常会产生较长的类名,但现代浏览器对选择器性能的优化已经很好

  2. CSS压缩:通过gzip等压缩,重复的BEM前缀压缩效果很好

/* 压缩前 */
.nav__item {}
.nav__link {}
.nav__dropdown {}

/* 压缩后类名前缀会被优化 */
  1. CSS-in-JS中的BEM:在styled-components等库中,BEM命名可能不太必要,因为样式已经自动隔离
const StyledButton = styled.button`
  /* 自动生成唯一类名 */
  padding: 10px 15px;
  
  ${props => props.primary && `
    background: blue;
  `}
`;

BEM的历史和发展

BEM方法论起源于Yandex在2009年前后的项目实践,最初是为了解决大型网站的CSS维护问题。随着前端工程化的发展,BEM逐渐演变为多种变体:

  1. 原始BEM:严格的命名约定
.block__element--modifier {}
  1. 宽松BEM:允许一定灵活性
.block-element--modifier {}
  1. BEM与其他模式结合:如BEMIT将BEM与ITCSS架构结合

Yandex官方BEM工具栈包括:

  • bem-core:核心库
  • bem-components:UI组件库
  • bem-make:构建工具

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步