BEM的50道阴影

特别声明:此篇文章由David根据的英文文章原名《Fifty Shades of BEM》进行翻译,整个译文带有我们自己的理解与思想,如果译得不好或不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:http://blog.kaelig.fr/post/48196348743/fifty-shades-of-bem以及作者相关信息

——作者:

——译者:David

当一种开发理念变得流行时,通常它也会被程序员们进行各种分支以适应各种不同的工作流和习惯。这无疑会导致各种版本的实现,情况看上去有些支离破碎。此时此刻,这种事情正发生在BEM上。在过去的几个月里我有幸亲自尝了尝不同“口味”的BEM。

虽然这篇博文和E.L.James的《格雷的50道阴影》(泽注:Fifty Shades of Grey据说是一部销量超过了《哈里波特》七部书的销量总和的全球畅销情色小说……)没有任何关系,但我认为它是个非常吸引人的标题。各位,这里没有任何情色内容(尽管一些开发者在使用了BEM方法论后发现他们的CSS代码变得十分性感)。

我们越来越普遍地使用BEM(即Block-Element-Modifier的缩写)这种方法来构建CSS。如果你还没有听说过BEM,我在之前的一篇博文 Please Respect the Global CSS Namespace 中涉及到这个概念的一些信息(你会在文章结尾处找到很多BEM的相关资源)。

在之前的6个月中我有机会在一些大项目中尝试BEM,并且遇到了许多不同的实现方式。

值的注意的是,在你使用BEM Tools构建你的前端CSS,JavaScript,BEMHTML时,BEM会从头到尾贯穿始终。本文不会涵盖BEM Tools这部分,而只关注如何在我们的CSS中应用BEM。

用BEM创建一个导航菜单

在代码中应用BEM的一个最显而易见的好处是通过阅读CSS代码(在不知道HTML结构的情况下)我们能获取到的信息比使用其他任何技术都多。如果我们想创建一个简单的导航菜单,用BEM的方法就是把选择器的名称分解成几个容易识别的部分,像下面这样:

  • Block (例如: navigation)
  • Element (例如: item)
  • Modifier (例如: active)

完全独立的块(CIB:Completely Independent Block )是众多有用的BEM结构之一:一个独立的模块应该是具有良好地可移植性,它有着自己的类名集。BEM可以很容易地把一个组件分解成多个部分,在阅读代码的时候你就能准确地知道它们所代表的意义:

.navigation {}
.navigation .item {}
.navigation .item.active {}

就这么简单!但在这段代码中隐藏着一个巨大的问题:子元素.item将会继承.navigation .item的样式。简单来说就是,如果我们在导航中再嵌入一个子导航,那么.subnavigation .item的元素就会从.navigation .item中继承样式。

一种快速修复的办法是用子选择符:

.navigation {}
.navigation > .item {}
.navigation > .item.active {}

效果不错!使用上面的方案,样式就只会作用到直接嵌套在.navigation中的.item元素(而不会“伤及”.subnavigation .item)。但是这样一来我们就过分依赖一个具体的DOM结构了,我们的代码成了“易碎品”而不易于继续发展进化。这样可不太好。同时,会造成.active这样常见的类面临很多冲突的风险。

使用BEM的低级标识符(low-level identifiers)这两个问题将会迎刃而解,同时也可以避免和其他UI组件冲突的风险:

.navigation {}
.navigation__item {}
.navigation__item--active {}

这个符号让元素之间的关系显而易见。它很容易让人想到HTML应该是个什么样的结构,同时它适用于很多有着自由语义的HTML结构。一种实现是把.navigation__item直接作为.navigation的子元素,但是也不一定非要这么做。如果我们想在里面再嵌入一个子导航,那么子导航里的菜单项就可以起名叫.subnavigation__item。这些子导航的菜单项不会受到.naviation__item中样式的影响(因为我们已经解决了级联的问题)。

一个理念多种实现

BEM主要是一种理念,它的发明者(Yandex)从未试图强制开发者使用任何具体的实现,符号或者是文件命名约定。事实上,有句话是:最合适的即是最好的。

听说Yandex的人想出好多种方法把这一理念应用到他们的CSS中。让我们通过那些最流行的符号来的探个究竟。

下划线和意义非凡的横线

Nocolas Gallager在他的博客“About HTML semantics and front-end architecture” 中第一次提到类似BEM的CSS命名方法,在Harry Robert的博客 MindBEMding – getting your head ’round BEM syntax中对BEM的命名方法进行了详尽地描述(在那篇博文中他的说法听上去很酷)。(译注:Harry Robert的这篇博文W3cplus也曾对其进行过翻译,详见:BEM思想之彻底弄清BEM语法

大概是下面这样的:

component-name
component-name--modifier-name
component-name__sub-object
component-name__sub-object--modifier-name

看上去真的很丑,但是,只要你肯稍稍思考一下就可以轻而易举地明白这些符号是如何工作的。

我已经在BBC的响应式新闻网站中应用这种命名方法,它看起来和《卫报》的响应式网站中所做的一样。同时,在一些平台重新设计的时候,我也在那些应用的某些部分用到了这些符号,尽管有些丑,但是它始终运行良好。

Montage的驼峰命名法

Montage HTML5 Framework中,大家最近参与的一个讨论引出了一些有趣的命名规范

“类名应该是像org-Componentorg-Component-childElement这种形式的。因此,对于一个进度条来讲,它的类命名应该是:montage-Progressmontage-Progress-bar。用双横线来表示组件的某种状态或某个变体。”

org-ComponentName
org-ComponentName--modifiername
org-ComponentName-subObject
org-ComponentName-subObject--modifiername

有趣的是Montage扩展了这种理念,他们用类名来表示一个子元素中的子元素(即孙子元素),也就如我们看到的.org-ComponentName-subObject-subChild标识符。

我个人觉得这种表示方法相比较第一种要好看很多。Nicolas Gallagher在他的CSS工具套装里也使用了这种方式。

对于Google Closure

我们用Google Closure编译我们的产品。为了配合和JavaScript开发者的习惯,我们需要一个与框架命名约定匹配的修饰符名。

当遇到不同的状态时Google Closure会怎么处理?在Google Closure的逻辑里的mouseover状态是一种MOUSEOVER类型的事件。这有助于我们定义自己的修饰符命名约定——全部大写!在我们的代码库中,修饰符会表示成 class-MODIFIER

除了这点细微的变化之外,其余的和Montage记号法如出一辙:

prefix-ComponentName
prefix-ComponentName-MODIFIERNAME
prefix-ComponentName-subObject
prefix-ComponentName-subObject-MODIFIERNAME

你会好奇这里的prefix代表什么?为了具有更加清晰的意义,我们在类名开始的地方用了前缀:

  • p-页面(Page) (应用于body元素的类),对可维护性不是那么重要的静态页面十分有用 —应该避免嵌套使用 (例: p-Homepage);
  • l-布局(Layout), 比如列(columning),包裹(wrappers) 和容器(containers)等等(例: l-Masthead, l-Footer);
  • c-组件(components )(例: c-Dropdown, c-Button…);
  • u-公共类(Utility classes) — 不会发生改变, 在代码的任何地方都不能重载。(例: u-textCenter, u-clearfix…);
  • js-JavaScript钩子:永远不应该出现在CSS中。

伴随着这些极具描述性的命名约定(灵感源自Jonathan Snook的SMACSS),我们相信自己可以完成一个更加强健的结构,开发者不会再那么轻易就因为混淆了层叠而发生错误。我相信时间会证明一切。

完全取决于你

如果你明天就想在自己的项目中尝试BEM,那么关于采用什么样的命名约定完全取决于你自己。

说实话,我很少见(到目前为止)两个项目中是使用相同的命名约定,却见过一个代码库中的许多项目用到了多个命名约定(通过查看类的命名,可以知道代码过去是如何编写的)。命名很容易变得过分随意——意识到这一点,我们就要工点时间对我们的代码做些处理,确保在整个项目中至少有某种一致性。

你在自己的项目中用过BEM么(或者是像BEM一样的标记法)?如果有,你的完美的编码和命名风格是什么样的?

译者手语:整个翻译依照原文线路进行,并在翻译过程略加了个人对技术的理解。如果翻译有不对之处,还烦请同行朋友指点。谢谢!

关于David

2009年开始接触前端开发,2011年组建创业团队——[五维互动],2012年团队被“收编”并更名[创影互动],遂只身来上海发展,现在就职于FlipScript。欢迎交流共勉:腾讯微博个人博客

如需转载烦请注明出处:

英文原文:http://blog.kaelig.fr/post/48196348743/fifty-shades-of-bem

中文译文:http://www.w3cplus.com/css/fifty-shades-of-bem.html

返回顶部