写在前面
最近一直在练习JS剑法有关的知识,有段时间没有碰CSS这把剑了。恰好有幸了解到BEM(Block Element Modifier)命名规范的相关内容,本来没在意,但我发现日常使用的微信的界面UI,居然也遵循了这一规范。
乱是乱了点,但这确实是WeUI,里面还展示了许多组件。那这个BEM规范是否值得好好学习一番呢?不管了,我先冲了!我花了点时间模仿淘宝写了一个电商的订单模块,先贴张效果图。
图标和样式参考的都是淘宝的界面,图标库我会在文末给出,接下来就通过这个小例子来体会一下BEM规范。
什么是BEM?
BEM — 是一种帮助开发者在前端开发中创建可重用组件和代码共享的方法。
通俗地说,BEM(Block )就是对CSS代码编写的一种约定。其中:
-
B(Block)为块元素,代表了本身有意义的一种封装实体,块与块之间可以嵌套使用。
命名:
<div class=".block"></div> -
E(ELement)为子元素,是块的一部分,其本身没有意义,但与其所关联的块在语义上有关系。
命名:
<div class=".block__element"></div> -
M(Modifier)为修饰符,修饰块元素或子元素,也就是说其可以与块或者子元素搭配使用,改变它们的状态。 命名:
<div class=".block__element--statu"></div>下面给出官网的例子:
<style> .form { } .form--theme-xmas { } .form--simple { } .form__input { } .form__submit { } .form__submit--disabled { } </style> <body> <form class="form form--theme-xmas form--simple"> <input class="form__input" type="text" /> <input class="form__submit form__submit--disabled" type="submit" /> </form> </bofy>这里解释一下,上面的
.form是一个块元素,代表一个表单实体,是单独的组件。.form__input和.form__submit分别代表了表单的输入框和提交元素,它们与块元素——表单使用双下划线__连接,表示这些是块元素的直接组成部分。对于.form__submit--disabled,按照之前的介绍我们可以知道这表示了一个修饰符,也就是在表单form下,提交元素submit的状态statu。值得注意的是,上述代码中还有
form--theme-xmas form--simple这种更为直接的结构,将块与修饰符直接使用--连接。注意,块和元素都能连接修饰符,因为它们都有单独的状态。不过这里似乎还有一个坑,也就是大家注意到的
-,这个单横线代表什么呢?难道也表示什么状态?一开始我也有点疑惑,明明官方文档中没有给出单横线-的解释,这里却使用了,这有什么用呢?我想告诉大家的是,这个单横线
-确实不属于BEM命名规范,因为form--theme-xmas form--simple表示的是表单中使用主题元素,主题风格为圣诞风格,这个圣诞风格并没有被定义为一种修饰符,而只是一种易于区分的标记,将form--theme-xmas看作是一个整体,它与上述的form--simple类型其实是一样的,只不过前者人为加了一个修饰符的后缀(标记),这个点容易先入为主。
结论: 能够单独存在,对块元素有整体影响的名词,比如:theme应该作为一个修饰符,使用--连接。而,依赖于块,离开块就失去其意义,且只对块的局部有影响的,比如:input元素自身,应当作为子元素,使用__连接。
对于新手来说,区分子元素和修饰符是一个难点,掌握了这个就不会再诟病BEM规范难且复杂了。
实战
接下来我们一起来看一下实战中如何运用BEM规范。
还是这张图,这里我参照了WeUI的命名方式,命名规范还是严格遵循BEM官方文档。分为 .order__hd和**.order__bd**两个部分,其中.order__hd又可以分为:.order__title和.order__desc,而.order__bd中:若干个.order__item。至于上面一栏功能,都是.function__bd中的.function__item。我们还是来看看HTML部分代码:
<div class="ds">
<div class="ds-function">
<div class="ds-function__hd"></div>
<div class="ds-function__bd">
<a href="#" class="ds-function__item">
<span class="ds-function__icons ds-function__icons--star">
<svg>...</svg>
</span>
<span class="ds-function__name ds-order__name-star"> 收藏 </span>
</a>
</div>
</div>
<div class="ds-order">
<div class="ds-order__hd">
<div class="ds-order__title">我的订单</div>
<div class="ds-order__desc">全部 ></div>
</div>
<div class="ds-order__bd">
<a href="#" class="ds-order__item">
<span class="ds-order__icons ds-order__icons--unpay">
<svg>...</svg>
<span class="ds-order__count ds-order__count--unpay">8</span>
</span>
<span class="ds-order__status ds-order__status--unpay">
待付款
</span>
</a>
</div>
</div>
</div>
这里包括了:上部的收藏图标示例,下部订单以及图标的示例,其他图标都是如法炮制。单独揣摩下这部分:
<div class="ds-order__bd">
<a href="#" class="ds-order__item">
<span class="ds-order__icons ds-order__icons--unpay">
<svg>...</svg>
<span class="ds-order__count ds-order__count--unpay">8</span>
</span>
<span class="ds-order__status ds-order__status--unpay"> 待付款 </span>
</a>
<div>
其中,ds代表整个电商模块,我在<a>标签内定义整个.order__item,放置图标,在图标右上角设置待付款订单的数量,最后在图标底部显示待付款字样。
.order是块元素,代表订单。
.order__bd是订单的子元素,代表订单界面的主体元素—主体内容。
.order__item也是订单的子元素,代表订单界面里面的个体元素—订单项,包含于主体元素.order__bd。
.order__icons、.order__count和.order__status三者都是隶属于.ds-order__item,其中.order__count包含于.order__icons。
.ds-order__status--unpay是一个修饰符,用于表示订单的状态具体为"未付款"。
继续看CSS部分的代码:
<style>
* {
margin: 0;
padding: 0;
outline: none;
}
body {
font-family: system-ui, -apple-system, sans-serif;
background-color: #f1cece;
height: 100vh;
}
a {
/*取消下划线*/
text-decoration: none;
color: #000;
}
.ds {
background-color: #ededed;
border-radius: 15px;
}
.ds-order {
background-color: rgb(255, 255, 255);
border-radius: 15px;
margin: 0 8px;
}
.ds-order__hd {
height: 40px;
display: flex;
align-items: center;
justify-content: space-between;
}
.ds-order__bd {
height: 80px;
display: flex;
}
.ds-order__title {
font-family: 微软雅黑;
padding-left: 10px;
font-weight: 600;
}
.ds-order__desc {
font-size: 14px;
color: grey;
padding-right: 10px;
}
.ds-order__item {
display: flex;
height: 100%;
width: 100%;
flex-direction: column;
align-items: center;
}
.ds-order__icons {
margin-top: 10px;
position: relative;
}
.ds-order__count {
width: 10px;
padding: 0px 5px;
border-radius: 50%;
background-color: transparent;
position: absolute;
top: -5px;
right: -5px;
z-index: 1;
}
.ds-order__count--unpay {
background-color: #ff8800;
}
.ds-order__count--send {
background-color: #fff;
}
.ds-function__bd {
display: flex;
height: 80px;
}
.ds-function__item {
display: flex;
height: 100%;
width: 100%;
flex-direction: column;
align-items: center;
justify-content: center;
}
</style>
flex-direction: column这里是将一列上的元素对齐,其他样式都不难,主要是先把结构搭好调整可以慢慢来。
分析清楚基本的结构层次,我们最后来谈谈基于BEM规范设计这种页面的难点:
-
难点一:命名,命名在BEM规范中是很重要的,命名的准确度直接影响了代码的层次性,命名的好坏体现了我们的规范程度。这个案例我参照了WeUI官方的结构。完整的WeUI页面结构通常包括三部分:page__hd、page__bd和page__ft,也就是页面的头、身体和尾部。
-
难点二:明确作用域,每个块、元素和修饰符应该规划好合理的边界,这需要我们更多地在项目上遵循这一规范,熟练使用就能掌握。也是参照着WeUI的页面命名,我快速理清了本案例的结构。
-
难点三:类名冗长,这是BEM追求规范化和明确化需要付出的代价,这个时候需要我们学会利用Sass、Less等CSS预处理器的特性,如
@extend和@include等,减少代码的重复,但这也需要我们花一些时间掌握。
BEM规范的优点
-
提高代码的可维护性:BEM规范使得项目开发模块化,也就是结构层次清晰,每个组件样式相对独立,减少了代码之间的耦合度。
-
提高代码的复用性:BEM官方鼓励我们向组件开发,一个完整的块结构在很多情况下可以轻易在别的项目复用,降低了开发成本。
-
易于拓展:在一个BEM高手的眼中,功能的改变只是基于块__元素--修饰符的CRUD,这种有套路的设定让一切变得更加简单。
总结
BEM — 一种帮助开发者在前端开发中创建可重用组件和代码共享的方法。包括块(block)、(element)元素和(modifier)修饰符,其中元素之间可以嵌套。
以上就是我在实战中学到的。在学习BEM规范过程中,我也被这种命名难度和阅读难度给整的很混乱。但是我认真梳理了一下,就发现BEM规范之所以在WeUI这些项目中被广泛应用,就是因为它在越复杂的项目中,所凸显的优势就越大。
由于笔力有限,关于BEM规范的许多热点问题请各位看官移步官网(getbem.com/)。
参考:
1.BEM — Naming (getbem.com)
2.iconfont-阿里巴巴矢量图标库