BEM是一种让你可以快速开发网站并对此进行多年维护的技术。
(一) 简介
一开始,Yandex公司推出的BEM,包括了规范以及其配套构建工具。
如今提到的BEM主要是指其中的规范,在BEM最新的推广页B中,对其的描述为:
BEM是一种命名方法,能够帮助你在前端开发中实现可复用的组件和代码共享
(二) BEM解决的问题
css的样式应用是全局性的,没有作用域可言。
考虑以下场景:
-
场景一:开发一个弹窗组件,在现有页面中测试都没问题,一段时间后,新需求新页面,该页面一打开这个弹窗组件,页面中样式都变样了,一查问题,原来是弹窗组件和该页面的样式相互覆盖了,接下来就是修改覆盖样式的选择器…又一段时间,又开发新页面,每次为元素命名都心惊胆战,求神拜佛,没写一条样式,F5都按多几次,每个组件都测试一遍…
-
场景二:承接上文,由于页面和弹窗样式冲突了,所以把页面的冲突样式的选择器加上一些结构逻辑,比如子选择器、标签选择器,借此让选择器独一无二。一段时间后,新同事接手跟进需求,对样式进行修改,由于选择器是一连串的结构逻辑,看不过来,嫌麻烦,就干脆在样式文件最后用另一套选择器,加上了覆盖样式…接下来又有新的需求…最后的结果,一个元素对应多套样式,遍布整个样式文件…
以往开发组件,我们都用“重名概率小”或者干脆起个“当时认为是独一无二的名字”来保证样式不冲突,这是不可靠的。 理想的状态下,我们开发一套组件的过程中,我们应该可以随意的为其中元素进行命名,而不必担心它是否与组件以外的样式发生冲突。
BEM解决这一问题的思路在于,由于项目开发中,每个组件都是唯一无二的,其名字也是独一无二的,组件内部元素的名字都加上组件名,并用元素的名字作为选择器,自然组件内的样式就不会与组件外的样式冲突了。
这是通过组件名的唯一性来保证选择器的唯一性,从而保证样式不会污染到组件外。
BEM的意思就是块(block)、元素(element)、修饰符(modifier),
是由Yandex团队提出的一种前端命名方法论。
这种巧妙的命名方法让你的CSS类对其他开发者来说更加透明而且更有意义。B
EM命名约定更加严格,而且包含更多的信息,它们用于一个团队开发一个耗时的大项目。
命名约定的模式如下:
.block {}
.block__element{}
.block--modifier {}
-
.block
代表了更高级别的抽象或组件。 -
.block__element
代表.block的后代,用于形成一个完整的.block的整体。 -
.block--modifier
代表.block的不同状态或不同版本。
其中,block表示的是独立的分块或组件;element表示每个block中更细粒度的元素;modifier则通常会用来表示该block或者element不同的类型和状态。
举个例子,例如我们有一个列表
<ul class="list">
<li class="item">learn html</li>
<li class="item underline">learn css</li>
<li class="item">learn js</li>
</ul>
列表容器的class为.list
,列表内每条记录的class为.item
,其中,还为第二个条记录添加了一个下划线.underline
。简单的css如下
.list {
margin: 15px;
padding: 0;
}
.list .item {
margin: 10px 0;
border-left: 3px solid #333;
font-size: 15px;
color: #333;
list-style: none;
}
.list .underline {
color: #111;
text-decoration: underline;
}
这样的命名方式,我们在阅读html时并不能迅速了解:.item
是只能在.list
中使用么,
它是仅仅定义在这个组件内的一部分么?.underline
是一个通用样式么,
我想修改列表的中underline的记录为红色,这会影响到项目其他地方么?
这时候,我们就可以使用BEM方式来命名我们的class
.list {
margin: 15px;
padding: 0;
}
.list__item {
margin: 10px 0;
border-left: 3px solid #333;
font-size: 15px;
color: #333;
list-style: none;
}
.list__item--underline {
color: #111;
text-decoration: underline;
}
<ul class="list">
<li class="list__item">learn html</li>
<li class="list__item list__item--underline">learn css</li>
<li class="list__item">learn js</li>
</ul>
这段代码的一大优势就是增加了它的自解释性:一定程度上,它的class名本身就是一个简易的文档。
这里还需要避免一个误区,BEM命名规范里,我们的CSS并不会关心HTML中dom元素的层级结构。
它的核心着眼点还是我们定义的块(block)、元素(element)、修饰符(modifier)这三部分。
因为关注点不同,所以一个block内的所有element,在CSS中并不会考虑层级,
因此也就没有.list__item__avatar
这种写法
而是把这个img也看作block中的元素.list__avatar
<ul class="list">
<li class="list__item">
![](avatar.png)
learn html
</li>
<li class="list__item list__item--underline">learn css</li>
<li class="list__item">learn js</li>
</ul>
从这个例子看一看出,CSS部分并不关心dom层级结构,而是在block下面有哪些element,这些element又有哪些modifier。
基于这个思想,我们可以知道,如果一个block里面含有其他block并不会违反BEM的原则。
例如上面这个列表的例子,其中头像avatar原本只是一个简单的element,
现在如果变成了一个很复杂的组件——包括图片、姓名和标签,那么可能会有这么一个block
<ul class="list">
<li class="list__item">
<div class="list__avatar">
<img class="list__head list__head--female" />
<span class="list__name"></span>
<span class="list__tag"></span>
</div>
learn html
</li>
<li class="list__item list__item--underline">learn css</li>
<li class="list__item">learn js</li>
</ul>
我们可以为avatar创建一个新的block
<ul class="list">
<li class="list__item">
<div class="avatar">
<img class="avatar__head avatar__head--female" />
<span class="avatar__name"></span>
<span class="avatar__tag"></span>
</div>
learn html
</li>
<li class="list__item list__item--underline">learn css</li>
<li class="list__item">learn js</li>
</ul>
那么你可能会有疑问,什么时候需要在将一个elment重新抽象为新的block呢?
仅仅当我们的dom元素变得很多的时候么?
其实,BEM中的block一定程度上可以理解为一个“独立的块”。
独立就意味着,把这一部分放到其他部分也可以正常展示与使用,它不会依赖其父元素或兄弟元素。
而在另一个维度上面来说,也就是视觉设计的维度,当UI设计师给出UI稿后,其中的一些设计元素或组件会重复出现,这些部分也是可以考虑的。
所以理解UI设计稿并不是指简单的还原,其中的设计原则与规范也值得揣摩。
从上面的简单介绍可以看出,BEM有着一些优点:
-
class的单一职责原则、开闭原则
-
模块化思想,一般来说遵循这个方法的组件可以迁移环境
-
一定程度上,避免命名的污染
-
自解释性。可以直观看出各个class之间的依赖关系以及它们的作用范围(.list__item和.list__item–underline都是依赖于.list的,因此它们不能脱离于.list存在)
当然,BEM仅仅是一种命名规范或建议。在没有约束的情况下,你随时都可以违反。
所以我们可以借助类似BEM-constructor的工具,既帮我们进行一定的约束,同时也省去一些繁琐的重复工作。
(三) 总结
BEM是一种命名规范,或者说是一种使用建议。
他的目的是帮助我们写出更易维护与协作的代码,更多的是在代码规范的层面上帮助我们解决CSS模块化中的问题。
然而,也不得不承认,它距离我们梦想中的CSS模块化还有这很长的距离。
但是无论如何,其中蕴含的一些组件化与CSS结构组织方式的想法也是值得我们去思考的。