BEM介绍
FridaS opened this issue · comments
BEM介绍
2018年04月02日
前两天腹肌哥哥抱怨我们的样式文件杂乱而难以维护时提到了BEM,于是决定对其一窥究竟。
BEM自称前端开发方法论(Frontend Development Methodology),提供了包括命名规则、CSS/JS模块化原则、构建工具在内的一套用于开发环节的方法。这篇文章只讨论其在CSS class name命名上的规范。
按照BEM的官方说明,
BEM是一种非常有用的、强大的、简单的命名规范,它使你的前端代码具有更高的可读性、更易于使用、更容易扩展、更健壮、更明确、更严格。
BEM实体
BEM代表 块(block)、元素(element)、修饰符(modifier),它们被称为BEM实体。
- 块(block)
块是本身具有意义的独立实体。在大多数情况下,任何独立的页面元素都可以被视作一个块。比如header
、container
、menu
、checkbox
、input
。
块可以包含其他块。比如,下图中的header块包含了logo块、navigation块和search块。
比快更细粒度的是元素(element)。
- 元素(element)
元素是块的组成部分,它表现为某一特定的功能。元素依赖块而存在、它只在其所属的块的上下文中有意义(脱离块就不能使用)。比如menu
块的item
元素、header
块的title
元素。
下图中,一个search块包括text input元素和search button元素。
- 修饰符(modifier)
一个“修饰符”可以理解为一个块或一个元素的特定状态,我们使用它来定义块或元素的不同的外观及行为。
举个例子来理解下:一个button块有两种颜色:红色和绿色,那么就可以定义两个修饰符:red
和green
,得到的完整的class name就是button--red
和 button--green
。
命名规则
BEM方法论提出者Yandex的命名规则:
- 每个BEM实体都有一个名称,由小写字母和数字组成,名称中的不同单词通过连字符(-)连接,如
property-editor
; - BEM中的CSS类名的第一部分是块的名称,块的名称作为CSS类名的命名空间;如表示菜单的块名称可以是
menu
,该快中的元素的CSS类名都会包含menu作为前缀; - 块中的元素的类名是在块的名称后加上元素的名称,通过两个下划线(__)分隔,如菜单块中的每个菜单项的名称是item,其类名即为
menu__item
; - BEM最多只有B+E+M三级,一个block下的所有element无论相互层级如何,都要摊开扁平地属于block,比如一个element-son是另一个element-father的子元素,那么正确的写法是block__element-son(而不是block__element-father__element-son!);
- 修饰符可以用来修饰块或元素,在CSS类名中,修饰符紧跟在所修饰的实体的名称之后,通过单个下划线(_)分隔;
- 有两种类型修饰符:布尔修饰符和名值对修饰符,布尔值修饰符表示某种状态,如
menu__item_selected
,其中selected是不二修饰符;名值对修饰符由名称和值两部分组成,通过单个下划线(_)分隔,如order_status_paid
对应 status 为 paid 的订单; - 一个CSS类名中不应该同时出现块和元素的修饰符。
BEM并没有限定必须使用怎样的命名规则,目前比较流行的是由Harry Roberts提出的命名规范(也是Google的Material Design Lite库使用的命名规则),其与Yandex规则的不同之处有:
- 布尔修饰符和其所修饰的实体名称之间通过两个连字符(—)来分隔;
- 不使用名值对修饰符。
<ul class="menu">
<li class="menu__item menu__item--selected">Menu Item 1</li>
<li class="menu__item">Menu Item 2</li>
<li class="menu__item">Menu Item 3</li>
</ul>
.menu{
list-style: none;
}
.menu__item{
font-weight: bold;
}
.menu__item--selected{
color:red;
}
为什么要用BEM
BEM的优点:
-
BEM特色的关键是块的相互独立,具有高度的可移植性和可复用性;
-
在BEM命名规则中,所有的CSS样式规则都只用一个类别选择器,因此所有样式规则的特异性(specificity)都是相同的,也就不存在复杂的优先级问题;
-
防止CSS嵌套过深;
-
每个CSS类名都很简单明了,可读性非常高;
-
类名的层次关系可以与DOM节点的树形结构相对应,读HTML结构时,能很容易地看出元素之间的依赖关系;
-
减少了类名冲突和副作用的可能性,没有如
.mod-xxx ul li
的写法带来的潜在的嵌套风险。
我在haitaowap工程上随意找了一段代码:
其样式为:
现在按照BEM改写它:
<div class="n-inGroupBuyShare">
<ul class="dialogs">
<li class="product">
<div class="head">
<img class="head__img" src="${headImg!''}" alt="">
</div>
<div class="content">
<img class="content__img" src="${imageUrl!''}" alt="">
<p class="content__desc">${goodsTitle}</p>
<p class="content__price">¥<em class="content__actual-price">${groupBuyPrice}</em></p>
</div>
</li>
</ul>
</div>
.head__img, .content__img{
width: 444px;height: 446px;
}
.content__desc{
margin-top: 7px;margin-bottom: 9px;height: 111px;overflow: hidden;
font-size: 26px;line-height: 37px;font-weight: bold;
}
.content__price{
display: flex;
height: 50px; align-items:center;
font-size: 28px;color: #e31436;
}
.content__actual-price{
margin-left:-4px;
font-size:52px;font-style:italic;margin-top:-13px;
letter-spacing:1px;font-weight:bold;
-webkit-font-smoothing: antialiased;text-rendering: optimizeLegibility;
}
争议
当然,有很多人不喜欢使用BEM,他们认为:
- 乍看之下,CSS类名长而复杂;
- 不习惯使用双下划线(__)或双横线(--)来命名;
- 这么长的命名,代码量增加了。(实际上通过gzip压缩后几乎可以忽略不计。)
总结
BEM并不能完美地解决所有的问题,但是其**可以借鉴:
- 团队内统一的命名方式;
- 高可读性、可重用性、可移植性;
- 类名扁平化,防止很深的类名嵌套等等。
如果认为其命名冗长不可取,那么完全可以取其精华、去其“糟粕”。
参考文章
- https://www.sitepoint.com/css-architecture-block-element-modifier-bem/
- http://www.ayqy.net/blog/bem-block-element-modifier/
- https://github.com/Tencent/tmt-workflow/wiki/%E2%92%9B-%5B%E8%A7%84%E8%8C%83%5D--CSS-BEM-%E4%B9%A6%E5%86%99%E8%A7%84%E8%8C%83
- http://getbem.com/introduction/
- https://www.ibm.com/developerworks/cn/web/1512_chengfu_bem/index.html
- https://www.zhihu.com/question/21935157
- http://cn.getbem.com/faq/
by Frida
依稀记得BEM2017年初就讨论过, 当时是子龙在做 kmui 的过程中提出来的, 然后就没有然后了...