BEM(Block, Element, Modifier) 是一个基于组件的 web 开发方法。它背后的思想是将 UI 拆分成独立的块(blocks)。这可以更容易和快速开发复杂的 UI ,并且无需复制粘贴就可以重用已有的代码。

Block

一个可以重用的功能上独立的页面组件。在 HTML 中,块用class属性来体现。

特点:

  • 块的名称描述了它的用途(“它是什么?”——menu或者是button),而不是它的状态(“它是什么样的?”——red或者big)。

例子

<!-- Correct. The `error` block is semantically meaningful -->
<div class="error"></div>

<!-- Incorrect. It describes the appearance -->
<div class="red-text"></div>
  • 块不应该影响它的环境,意味着你不应该设置外部几何形状(margin)或者定位。
  • 你也不应该用 CSS 标签或者 ID 选择器。

这能保证块的独立性,能够让它们在不同的地方重用。

使用块的规则

嵌套

  • 块可以嵌套使用。
  • 可以嵌套任意层。

例子

<!-- `header` block -->
<header class="header">
    <!-- Nested `logo` block -->
    <div class="logo"></div>

    <!-- Nested `search-form` block -->
    <form class="search-form"></form>
</header>

Element

块里面复合的部分,不能与块分开使用。

特点:

  • 元素的名称描述了它的用途(“它是什么?”——item或者是text),而不是它的状态(“它是什么样的?”——red或者big)。
  • 一个元素完整的名称的结构应该是block-name__element-name。元素名称和块名称使用双下划线(__)分隔的。

例子

<!-- `search-form` block -->
<form class="search-form">
    <!-- `input` element in the `search-form` block -->
    <input class="search-form__input">

    <!-- `button` element in the `search-form` block -->
    <button class="search-form__button">Search</button>
</form>

使用元素的规则

嵌套

  • 元素可以嵌套使用。
  • 可以任意数目的嵌套层级。
  • 元素总是块的一部分,而不属于其他元素。这意味着元素名称不能定义成几层,比如block__elem1__elem2

例子

<!--
    Correct. The structure of the full element name follows the pattern:
    `block-name__element-name`
-->
<form class="search-form">
    <div class="search-form__content">
        <input class="search-form__input">

        <button class="search-form__button">Search</button>
    </div>
</form>

<!--
    Incorrect. The structure of the full element name doesn't follow the pattern:
    `block-name__element-name`
-->
<form class="search-form">
    <div class="search-form__content">
        <!-- Recommended: `search-form__input` or `search-form__content-input` -->
        <input class="search-form__content__input">

        <!-- Recommended: `search-form__button` or `search-form__content-button` -->
        <button class="search-form__content__button">Search</button>
    </div>
</form>

块的名称定义了命名空间,保证了元素是依赖于块的(block__elem)。

一个块在 DOM 树中可以有一个嵌套的元素的结构:

例子

<div class="block">
    <div class="block__elem1">
        <div class="block__elem2">
            <div class="block__elem3"></div>
        </div>
    </div>
</div>

然而,在 BEM 中,这个块的结构是被当做一串元素的扁平列表。

例子

.block {}
.block__elem1 {}
.block__elem2 {}
.block__elem3 {}

这种结构使得如果你想要改变块的 DOM 结构,不需要对每个元素的代码做出修改:

例子

<div class="block">
    <div class="block__elem1">
        <div class="block__elem2"></div>
    </div>

    <div class="block__elem3"></div>
</div>

块的结构改变了,可是元素的规则以及它们的名称没有改变。

成员身份

一个元素总是一个块的一部分,你也不应该在块以为单独使用它。

例子

<!-- Correct. Elements are located inside the `search-form` block -->
<!-- `search-form` block -->
<form class="search-form">
    <!-- `input` element in the `search-form` block -->
    <input class="search-form__input">

    <!-- `button` element in the `search-form` block -->
    <button class="search-form__button">Search</button>
</form>

<!--
    Incorrect. Elements are located outside of the context of
    the `search-form` block
-->
<!-- `search-form` block -->
<form class="search-form">
</form>

<!-- `input` element in the `search-form` block -->
<input class="search-form__input">

<!-- `button` element in the `search-form` block-->
<button class="search-form__button">Search</button>

可选性

一个元素对于块组件来说是可选的。不是所有块都有元素。

例子

<!-- `search-form` block -->
<div class="search-form">
    <!-- `input` block -->
    <input class="input">

    <!-- `button` block -->
    <button class="button">Search</button>
</div>

我应该建立一个块还是元素?

  1. 如果一段代码可能会被重用,而它也不依赖于正在被实现的其他页面组件,你应该建立一个块。
  2. 如果这段代码不能在父实体(块)外独立使用,你应该建立一个元素。

此外,有时候元素还必须要被拆分为更小的部分——子元素,这通常是为了简化开发。在 BEM 中,你不能创建元素的元素。这种情况下,你不应该建立一个元素,而应该是一个服务块。

Modifier

一个定义外表、状态、块或者元素的行为的实体。

特点:

  • 修饰符的名称应该描述它的外表(“什么大小?”或者“哪个主题?”和其他——size_s或者theme_islands),它的状态(“它跟其他的区别是什么?”——disabled,focused等等)和它的行为(“它是怎么表现的?”或者“它是如何相应用户的?”——比如directions_left-top)。
  • 修饰符的名称跟块或者元素的名称使用单下划线(_)分隔的。

修饰符的类型

布尔值

  • 只有当修饰符的出现与否是很重要,并且它的值无关紧要时使用。例如:disabled。如果一个布尔值修饰符在的话,它的值应该是true
  • 修饰符完整的名称结构应该遵循以下的样式:
    • block-name_modifier-name
    • block-name__element-name_modifier-name

例子

<!-- The `search-form` block has the `focused` Boolean modifier -->
<form class="search-form search-form_focused">
    <input class="search-form__input">

    <!-- The `button` element has the `disabled` Boolean modifier -->
    <button class="search-form__button search-form__button_disabled">Search</button>
</form>

键值

  • 当修饰符的值很重要时用。例如“用islands设计主题的菜单”:menu_theme_islands
  • 修饰符完整的名称结构应该遵循以下的样式:
    • block-name_modifier-name_modifer-value
    • block-name__element-name_modifer-name_modifier-value

例子

<!-- The `search-form` block has the `theme` modifier with the value `islands` -->
<form class="search-form search-form_theme_islands">
    <input class="search-form__input">

    <!-- The `button` element has the `size` modifier with the value `m` -->
    <button class="search-form__button search-form__button_size_m">Search</button>
</form>

<!-- You can't use two identical modifiers with different values simultaneously -->
<form class="search-form
             search-form_theme_islands
             search-form_theme_lite">

    <input class="search-form__input">

    <button class="search-form__button
                   search-form__button_size_s
                   search-form__button_size_m">
        Search
    </button>
</form>

使用修饰符的规则

修饰符不能单独使用

从 BEM 的角度,修饰符不能脱离块或者元素使用。一个修饰符应该改变外观,行为,或者实体的状态,而不是取代它。

例子

<!--
    Correct. The `search-form` block has the `theme` modifier with
    the value `islands`
-->
<form class="search-form search-form_theme_islands">
    <input class="search-form__input">

    <button class="search-form__button">Search</button>
</form>

<!-- Incorrect. The modified class `search-form` is missing -->
<form class="search-form_theme_islands">
    <input class="search-form__input">

    <button class="search-form__button">Search</button>
</form>

混合

在单独的 DOM 节点上使用不同的 BEM 实体的一个技术。

混合可以让你:

  • 无需复制代码就可以结合多个实体的行为和样式。
  • 基于已有的代码语义化的创建新的 UI 组件。

例子

<!-- `header` block -->
<div class="header">
    <!--
        The `search-form` block is mixed with the `search-form` element
        from the `header` block
    -->
    <div class="search-form header__search-form"></div>
</div>

在这个例子中,我们混合了search-form块和header块里的search-form元素的行为和样式。这种方法可以让我们对header__search-form元素设置外部的几何形状和定位,同时让search-form块本身保持通用。结果是,我们可以在不同的环境中使用这个块,因为它没有指定任何内编辑。这就是为什么我们可以单独的调用它。

文件结构

BEM 采用的组件方法同样应用给文件结构中的项目。块、元素和修饰符的实现被分到独立的文件中,意味着我们可以单独的连接它们。

特点:

  • 一个独立的块对应一个单独的目录。
  • 块和目录的名称一样。例如,header块在header/目录中,menu块在menu/目录中。
  • 块的实现被分到独立的稳重中。例如heaer.cssheader.js
  • 块目录是放有它的元素和修饰符的子目录的根目录。
  • 元素目录的名称以双下划线(__)开头。例如header/__logo/menu/__item/
  • 修饰符目录的名称以单下划线(_)开头。例如,header/_fixed/menu/_theme_islands/
  • 元素和修饰符的实现被分到独立的文件。例如header__input.jsheader_theme_islands.css

例子

search-form/                           # Directory of the search-form

    __input/                           # Subdirectory of the search-form__input
        search-form__input.css         # CSS implementation of the
                                       # search-form__input element
        search-form__input.js          # JavaScript implementation of the
                                       # search-form__input element

    __button/                          # Subdirectory of the search-form__button
                                       # element
        search-form__button.css
        search-form__button.js

    _theme/                            # Subdirectory of the search-form_theme
                                       # modifier
        search-form_theme_islands.css  # CSS implementation of the search-form block
                                       # that has the theme modifier with the value
                                       # islands
        search-form_theme_lite.css     # CSS implementation of the search-form block
                                       # that has the theme modifier with the value
                                       # lite

    search-form.css                    # CSS implementation of the search-form block
    search-form.js                     # JavaScript implementation of the
                                       # search-form block

这样的文件结构使得维护和重用代码更容易。

分支的文件结构假定在生产环境中代码会被组装成项目文件。

你不一定要遵循推荐的文件结构。你可以用其他遵守 BEM 原则的项目结构来组织你的文件,比如: