likes
comments
collection
share

CSS BEM (Block, Element, Modifier) 详解BEM 是一种强大的 CSS 命名方法论,它通

作者站长头像
站长
· 阅读数 42

导读

BEM(Block, Element, Modifier)是一种命名约定,旨在提高前端代码的可读性、可维护性和可复用性。BEM 由俄罗斯的 Yandex 团队提出,随着前端开发的复杂性增加,BEM 已成为一种非常流行的 CSS 组织方法。

什么是 BEM?

BEM 是一种基于组件的 CSS 命名约定,旨在让开发者更容易地管理样式和 HTML 结构。BEM 将用户界面的各个部分划分为“块”、“元素”和“修饰符”,并为它们提供标准化的命名方式。

/* Block */
.block {}

/* Element */
.block__element {}

/* Modifier */
.block--modifier {}
  • Block(块) :代表一个独立的功能单元,它可以是一个页面组件,如导航栏、按钮、表单等。
  • Element(元素) :是 Block 的一部分,完成特定功能,不能脱离 Block 独立存在,例如按钮中的图标、表单中的输入框。
  • Modifier(修饰符) :用于改变 Block 或 Element 的外观或行为,用于表示状态、属性或变体,例如按钮的大小、颜色或状态(如激活、禁用)。

BEM 的目的是仅从其名称就告诉其他开发人员更多关于标记的作用的信息。通过阅读一些包含 BEM 类的 HTML,您可以看到这些块是如何关联的(如果有的话);某些东西可能只是一个组件,某些东西可能是该组件的子元素或元素,某些东西可能是该组件的变体或修饰符。

BEM 的命名规则

BEM 的命名由三部分组成,每一部分都使用不同的符号分隔,确保命名的一致性和明确性。

  1. Block:块的名称。
  2. Element:块名称与元素名称之间用两个下划线 __ 连接。
  3. Modifier:块或元素名称与修饰符名称之间用两个连字符 -- 连接。

BEM 代码示例:

/* Block */
.button {
    background-color: blue;
    color: white;
    padding: 10px 20px;
    border: none;
    cursor: pointer;
}

/* Element */
.button__icon {
    margin-right: 8px;
}

/* Modifier */
.button--large {
    padding: 15px 30px;
}

.button--primary {
    background-color: green;
}

在这个例子中:

  • .button 是 Block,表示一个按钮组件。
  • .button__icon 是 Element,表示按钮中的一个图标元素。
  • .button--large.button--primary 是 Modifiers,表示按钮的不同尺寸和颜色变体。

为什么使用 BEM?

BEM 提供了一个清晰的命名结构,使代码更易于阅读和维护。它特别适用于大型项目和团队协作开发。以下是 BEM 的几个主要优点:

  • 可读性高:通过 BEM 的命名方式,开发者可以直观地理解每个类的作用,减少了代码阅读的难度。
  • 可维护性好:BEM 的结构化命名方式使得代码更容易扩展和修改,避免了意外的样式冲突。
  • 高复用性:通过明确的命名,BEM 促进了组件的复用,减少了重复代码的编写。

实例对比

以下是采用非 BEM 命名方式的 CSS 代码:

.person {}
.hand {}
.female {}
.female-hand {}
.left-hand {}

这些都有意义,但有些脱节,或者说意义不太明确。以 .female 为例;女性是什么?那么 .hand 呢?钟表的指针?或者是指纸牌游戏中的一只手?我们使用 BEM 命名方式调整一下之前的 CSS 代码:

.person {}
.person__hand {}

.person--female {}
.person--female__hand {}

.person__hand--left {}

顶层块是“人”,它具有元素,例如“手”。人也有变体,例如女性,而这种变体又具有元素,如女人的手。手又可以有具体的修饰符,指定是“左手”。

怎么样?通过使用 BEM,我们可以使 CSS 代码更具描述性,同时也可以有更加明确地意义。BEM 是不是非常强大?

BEM 的实际应用场景

BEM 在以下几个场景中特别有用:

1. 组件化开发

在组件化开发中,BEM 可以帮助将样式和结构分离,确保每个组件独立且易于管理。例如,导航栏、侧边栏、卡片组件等都可以使用 BEM 进行命名和组织。

代码示例

以下是侧边栏的组件代码示例:

/**
 * base-aside.less - BaseAside 组件样式
 * =============================================================
 * Created By: Yaohaixiao
 * Update: 2022.10.8
 */
.base-aside {
  margin: 0;
  height: 100%;
  box-sizing: border-box;
  transition-duration: 0.2s;
  overflow: auto;
  
  /* Element */
  &__header,
  &__footer {
    height: 48px;
  }
  
  &__content {
    min-height: 400px;
    overflow: hidden; 
  }

  /* Element */
  &--mini {
    width: 120px;
  }

  /* Modifier */
  &--small {
    width: 160px;
  }

  &--regular {
    width: 208px;
  }

  &--medium {
    width: 240px;
  }

  &--large {
    width: 280px;
  }

  &--huge {
    width: 320px;
  }

  &--flex {
    display: flex;
    flex-direction: column;
    flex-wrap: nowrap;
    align-self: auto;
    align-items: center;
  }
}

通过代码我们可以很清楚地了解到到侧边,这个侧边栏有页头、内容和页脚3个子元素,并且侧边栏有不同的宽度修饰器和 flex 布局的能力。

2. 避免全局样式污染

在大型项目中,全局样式可能会导致样式冲突,BEM 的命名方式有效地将样式限制在特定的块内,减少了这种风险。

代码示例

假设我们在一个项目中需要创建两个不同风格的按钮组件,一个用于主操作按钮(primary),另一个用于次要操作按钮(secondary)。通过 BEM,我们可以确保这两个按钮的样式不会互相干扰或影响到全局的样式。

<div class="button-group">
    <button class="button button--primary">
        <span class="button__icon">🔥</span>
        Primary Button
    </button>
    <button class="button button--secondary">
        <span class="button__icon">🌟</span>
        Secondary Button
    </button>
</div>
/* Block */
.button {
    padding: 10px 20px;
    border: none;
    cursor: pointer;
    font-size: 16px;
    display: inline-flex;
    align-items: center;
}

/* Element */
.button__icon {
    margin-right: 8px;
}

/* Modifier for primary button */
.button--primary {
    background-color: blue;
    color: white;
}

/* Modifier for secondary button */
.button--secondary {
    background-color: grey;
    color: black;
}
代码解读

独立的样式块

.button 是一个独立的块(Block),定义了按钮的通用样式。它不依赖于任何外部全局样式,因此可以安全地在不同的上下文中复用。

局部元素样式

.button__icon 是按钮中的一个元素,负责为图标提供特定的样式。由于使用了 BEM 的命名规则,这个样式仅影响 .button 内部的图标,不会影响其他图标或元素。

修改器样式

.button--primary.button--secondary 是两个修饰符,分别用于定义主按钮和次要按钮的不同外观。通过 BEM 的修饰符命名规则,这些样式变化是局部的,不会影响其他非 .button 组件的样式。

通过 BEM 命名方式有效地避免全局样式污染:

  • 样式隔离:每个按钮的样式都被限制在它自己的命名空间中,避免了对全局样式的污染。例如,.button--primary 的背景色修改不会影响页面上其他未使用 BEM 命名规范的按钮。
  • 样式复用:即使在其他页面或组件中再次使用 .button,它的样式也是独立的,不会因为其他组件或页面的样式变化而受到影响。
  • 可维护性:当需要修改或扩展按钮的样式时,可以直接针对具体的块、元素或修饰符进行操作,而不必担心影响到其他无关部分。

3. 团队协作

在团队开发中,统一的命名规范有助于所有开发者保持一致的代码风格,减少了沟通成本和误解。

BEM 的扩展和变种

BEM 是一个灵活的命名规范,在实际应用中,开发者可以根据项目需求进行调整。例如,有些团队可能会缩短 BEM 中的连字符和下划线,或者结合其他命名规范如 OOCSS(面向对象的 CSS)或 SMACSS(可扩展与模块化的结构化 CSS)一起使用。

BEM 的缺点

尽管 BEM 有很多优点使用和扩展也很方便灵活,但它也有一些缺点:

1. 类名冗长

BEM 的命名方式非常详细,这在一定程度上保证了样式的清晰和隔离性,但同时也会导致类名过长,尤其是在处理深度嵌套或复杂的组件时。

示例:复杂的嵌套结构

假设我们有一个用于展示文章的组件,它包含标题、作者信息、发布时间和正文内容。在 BEM 的命名方式下,这些元素可能会出现非常长的类名。

HTML 结构
<div class="article">
    <h1 class="article__title">BEM Naming Convention</h1>
    <div class="article__meta">
        <span class="article__meta-author">John Doe</span>
        <span class="article__meta-date">2024-08-18</span>
    </div>
    <div class="article__content">
        <p class="article__content-text">This is a paragraph of the article content...</p>
    </div>
</div>
CSS 样式
.article {
    margin-bottom: 20px;
}

.article__title {
    font-size: 24px;
    margin-bottom: 10px;
}

.article__meta {
    font-size: 14px;
    color: grey;
}

.article__meta-author {
    font-weight: bold;
}

.article__meta-date {
    margin-left: 10px;
}

.article__content {
    margin-top: 20px;
}

.article__content-text {
    line-height: 1.6;
}
代码分析
  • 类名冗长:如 .article__meta-author.article__content-text 等类名,由于 BEM 的结构化命名要求,这些类名变得相当冗长,特别是在 HTML 结构复杂时,会导致 CSS 文件和 HTML 文件的可读性下降。
  • 样式重复:如果我们有多个类似的组件,可能会出现样式的重复和命名的冗余,这会使得 CSS 文件变得冗长和难以维护。

2. 样式嵌套复杂

BEM 的设计初衷是避免深层次的嵌套,但在实际应用中,特别是在复杂 UI 组件中,BEM 可能会导致样式结构变得复杂,并且难以管理。

示例:嵌套样式过深

假设我们在一个表单中使用了 BEM,包含输入框、标签和错误信息。为了遵循 BEM,我们可能会创建非常复杂的嵌套结构。

HTML 结构
<form class="form">
    <div class="form__group">
        <label class="form__label" for="username">Username</label>
        <input class="form__input" id="username" type="text">
        <span class="form__error">This field is required.</span>
    </div>
    <div class="form__group">
        <label class="form__label" for="password">Password</label>
        <input class="form__input" id="password" type="password">
        <span class="form__error">This field is required.</span>
    </div>
</form>
CSS 样式
.form__group {
    margin-bottom: 20px;
}

.form__label {
    display: block;
    margin-bottom: 5px;
}

.form__input {
    width: 100%;
    padding: 10px;
    border: 1px solid #ccc;
}

.form__error {
    color: red;
    margin-top: 5px;
    font-size: 12px;
}
代码分析
  • 深度嵌套:在这个例子中,我们已经看到 BEM 的命名方式导致了多层嵌套(如 .form__group .form__label)。在更复杂的表单或组件中,这种嵌套会变得更加深,使得 CSS 文件的层级结构变得复杂。
  • 可读性下降:当嵌套结构过深时,BEM 的命名方式会让 CSS 代码变得繁琐,降低了可读性。开发者需要花费更多的时间理解和维护这些样式。

3. 重复的样式规则

在一些情况下,BEM 的命名规范可能会导致样式规则的重复,特别是在处理相似的组件时。

示例:相似组件的样式重复

假设我们有两个不同类型的按钮组件,一个用于提交表单,另一个用于取消操作。尽管它们在外观上相似,但由于不同的语义需求,BEM 命名会导致样式的重复。

HTML 结构
<button class="button button--submit">Submit</button>
<button class="button button--cancel">Cancel</button>
CSS 样式
.button {
    padding: 10px 20px;
    border: none;
    cursor: pointer;
    font-size: 16px;
}

.button--submit {
    background-color: green;
    color: white;
}

.button--cancel {
    background-color: red;
    color: white;
}
代码分析
  • 样式重复:尽管 .button--submit.button--cancel 共享许多相同的样式(如 paddingfont-size 等),但由于 BEM 的命名规范,必须为每个修饰符定义独立的类名。这可能会导致样式规则的重复,增加了 CSS 文件的大小。

4. 学习曲线

对于新手或没有接触过 BEM 的开发者来说,理解和遵循 BEM 规范可能需要一些时间。学习曲线和学习成本会高一些。

总结

BEM 是一种强大的 CSS 命名方法论,它通过规范化的命名方式帮助开发者编写更易读、易维护和复用的代码。在现代前端开发中,尤其是大型项目和团队协作中,BEM 已成为一种非常有效的 CSS 组织方式。

通过理解和应用 BEM,你可以大大提升项目的可维护性和开发效率,并减少在样式管理方面的复杂性。尽管 BEM 有一些小的缺点,但其带来的好处远远超过这些不足,使得 BEM 成为许多前端开发者的首选方法之一。

转载自:https://juejin.cn/post/7405834903370154022
评论
请登录