kangkk

BACK HOME

BEM

当我们构建一个小的web应用的时候,怎样合理的组织页面样式并不是一个大的问题,而一但web应用体量逐渐增大的时候,就需要一定的规范和技巧来减少大的应用开发过程中front-end developersback-end developers的联调消耗,应对这种场景由此产生了SMACSS[1]、OOCSS[2]以及BEM[3]等技术。

BEM Methodology will massively improve code maintainability and speed up the development process[4].

BEMBlockElementModifier的缩写,其思想是通过模块化命名dom元素进行css样式的管理,以增强css和类名的可读性和易用性(所以这种命名方式只能用于classes而不能用于IDs)。

Block

这里的Block是抽象的产物,按照Josh Medeski的说法:‘The block is the container or context where the element finds itself. [5]’,BEM解释在整体web应用中任何一个模块都是一个对象,即Block(模块对象),每一个模块一一对应相应的css样式,比如一个简洁的百度页面:

百度

将它的每一部分都进行边框线划分:

百度

此时按照BEM的思想,每一个边框内的元素都是一个Block对象,彼此之间相互独立又存在相互嵌套。假设其中的某个div存在.content类名,中间嵌套input

<div class="content">
  <input type="text" class="search">
</div>

其css样式:

.content {/* Styles */}
.search {/* Styles */}

看上去和我们平常组织的css样式写法并无区别,其中差异更多的是思想上的不同,也就是为下面ElementModifier概念的引申进行铺垫。

Element

这里我们期望Element代表一个功能性的事件元素,比如:

<div class="content__section">
  <input type="text" class="search__input">
</div>

其css样式:

.content__section {/* Styles */}
.search__input {/* Styles */}

Modifier

Modifier用于修饰,

<div class="content__section--featured">
  <input type="text" class="search__input--icon">
</div>

其css样式:

.content__section--featured {/* Styles */}
.search__input--icon {/* Styles */}

约定

Block命名通常应用常见的.content.header.footer.sidebar.area等,有时可能我们期望Block的命名能够更长且详尽一些,如.content变为.content-box,即约定可以用-来延长Block

.content-box {/* Styles */}

BlockElement之间连接用__,或者说每一个Element都是由__开始的。

.content__section {/* Styles */}

BlockModifier之间连接用--,如section--featured。另一个约定的规范是用_进行连接,如section_featured

.section--featured {/* Styles */}

通常我们在一个div的.header标签里可能添加了需要修饰的.header--fix,但是不是所有的.header功能性类模块都要进行.header--fix处理,所以最好如下:

<div class="header header--fix">
  <!-- html -->
</div>

BEM的命名替换方案有Harry Roberts styleCamelCase style

Harry Roberts style示例:

.block-name__element-name--modifier-name {/* Styles */}

约定规范的原文如下:

CamelCase style示例:

.BlockName__ElementName_ModifierName {/* Styles */}

实践

在我所阅读的《How to Use BEM Methodology》一文中有张图片形象的展示了如何应用BEM技术构建合理规范的UI,并列举了XMLJSONHTML之间的对应关系。

BEM Example in Different Formats

XML:

<block:header>
  <block:logo/>
  <block:search-from>
    <block:input/>
    <block:button/>
  </block>
  <block:lang-switcher/>
</block>

JSON:

{
  block: 'header',
  content: [
    { block: 'logo' },
    {
      block: 'search-form',
      content: [
        { block: 'input' },
        { block: 'button' }
      ]
    },
    { block: 'lang-switcher' }
  ]
}

HTML:

<header class=”header”>
  <img class=”header__logo”>
  <form class=”header__search-from”>
    <input class=”header__search-from__input” type=”input”>
    <button class=”header__search-from__button” type=”button”>
  </form>
  <div class=”header__lang-switcher”></div>
</header>

从图中我们可以看出BEM更倾向于模块对象(Block)语义化,但这里其实也是有争议的,比如Thierry Koblentz就提出:'If you check the W3C's "Tips for Webmasters" ,where it says "Good names don't change," you'll see that the argument is about maintenance, not semantics per se[6]'的论证观点用于阐述HTML标签的类名不应该过分强调语义化。

TOMISLAV MATIJEVIĆ对于BEM有一个形象的比喻,他将一个Block看作是一个人(person)。

female

人具有身体部位和不同性别,这里假定描述一个女生(female)的手部(hand)、腿部(leg)和左手(hand-left)。

female

用SASS描述CSS:

.person {
  &__hand {/* Styles */}
  &__leg {/* Styles */}
  &--female {
    /* Styles */
    &__hand {
      /* Styles */
      &--left {/* Styles */}
    }
    &__leg {/* Styles */}
  }
}

如果想更快速的书写样式,TOMISLAV MATIJEVIĆ集成了SASS书写BEM的方法:

// Block Element
// @param {String} $element - Element's name
@mixin element($element) {
    &__#{$element} {
        @content;
    }
}
// Block Modifier
// @param {String} $modifier - Modifier's name
@mixin modifier($modifier) {
    &--#{$modifier} {
        @content;
    }
}
.person {
  @include element('hand') {/* Person hand */}
  @include element('leg') {/* Person leg */}
  @include modifier('female') {
    /* Person female */
    @include element('hand') {
      /* Person female hand */
      @include modifier('left') {
        /* Person female left hand */
      }
    }
  }
}

输出效果:

.person__hand {/* Person hand */}
.person__leg {/* Person leg */}
.person--female {/* Person female */}
.person--female__hand {/* Person female hand */}
.person--female__hand--left {/* Person female left hand */}

构建BEM

构建需要使用ENB,具体方法官网上也有详尽描述:Starting your own BEM project

CssReset

Normalize.css[7] CSS Tools[8] aliceui[10]

具体的css reset选择可以阅读John W. Long的Modular CSS typography[9],也许可以提供一些灵感。

参考

References