关于BEM
BEM是Block Element Modifier缩写, BEM通过以下3个维度来进行CSS设计及命名的.
- Block 代码里大括号
- Element 大括号里的元素
- Modifier 标记上述2个的状态
目标
- BEM中特别以MindBEMding命名规则为基础
- 使用SCSS
- 通过基本的简单类来设计
- SCSS与HTML完美分离
命名规则
类block__element–modifier形式,block与element之间通过2个’_’连接,element与modifier之间通过2个’-‘连接.
block为多个单词时,单词与单词之间通过1个’-‘连接. element, modifier同样处理.
比如:
block名为artile-list
中有个叫artile-title
的element时, 这个element的类名为article-list__article-title
文件规则
以1个block
为单位生成对应SCSS文件. 反之如果1个SCSS文件里定义多个block
的话, 则说明违反的设计原则. 文件名为 block名字
.scss.
例如上述的block article-list
, 它所对应的文件名为article-list.scss
.
在CSS中所有的名称都是全局有效, 因为命名重复导致可维护性下降. 在BEM中通过严格的命名规则来解决这个问题. 不管element名称重复多少个都没有影响.
但是, 得避免block名即文件名重复. 遵守以1个文件对应1个block
的原则, 就可以很好的避免block
名称重复的问题了.
SCSS
规则
- 使用嵌套和父节点名称来定义element
- element内部不允许嵌套定义其它element
- 使用嵌套和父节点名称来定义modifier
- modifier使用placeholder selector,来描述差异的部分
使用嵌套和父节点名称来定义element
比如article-list__article-title
可定义如下
.article-list {
width: 100%;
&__article-title {
font-size: 20px;
}
}
element内部不允许嵌套定义其它element
假设有HTML如下
<div class="article-list">
<div class="article-list__article-title">
タイトル
<span class="article-list__article-subtitle">サブタイトル</span>
</div>
</div>
下面是违反BEM命名规则的范例
.article-list {
width: 100%;
&__article-title {
font-size: 20px;
&__article-subtitle {
font-size: 16px;
}
}
}
在HTML中,article-subtitle
是article-title
的子节点,但如果在SCSS中也article-title
中嵌套的话,即导致SCSS不能很好的将SCSS和HTML内容分离出来。
为了使HTML和SCSS分离, 不管HTML构造如何变化, element应避免关联除block以外的父节点。 SCSS写法如下
.article-list {
width: 100%;
&__article-title {
font-size: 20px;
}
&__article-subtitle {
font-size: 16px;
}
}
使用嵌套和父节点名称来定义modifier
假设想控制article-title
的文字颜色为是否为红色时, 则需要嵌套定义article-title
的modifier
.article-list {
width: 100%;
&__article-title {
font-size: 20px;
&--red {
// 后续章节定义,此处无任何功能
}
}
&__article-subtitle {
font-size: 16px;
}
}
对应的HTML内容如下
<div class="article-list">
<div class="article-list__article-title">
非红色标题
<span class="article-list__article-subtitle">子标题</span>
</div>
<div class="article-list__article-title--red">
红色标题
<span class="article-list__article-subtitle">子标题</span>
</div>
</div>
modifier使用placeholder selector,来描述差异的部分
当article-list__article-title--red
样式只是颜色为红色时, 其他样式全部和article-list__article-title
保持一致时,定义如下
.article-list {
width: 100%;
&__article-title {
font-size: 20px;
&--red {
font-size: 20px;
color: red;
}
}
&__article-subtitle {
font-size: 16px;
}
}
这里font-size: 20px;
是公共的组成部分, 这样子定义带的缺点就是导致公共组成内容重复增加。 当公共部分变化时, 保持一致所带来的代码维护就变得相当麻烦了。 当modifier里增加gren
, blue
等其他颜色时, 就会更难维护。那么如何避免呢?
为了避免上述状况,这里就需要借助placeholder selector
来管理公共部分, modifier针对每个element做差异处理。
.article-list {
width: 100%;
%__article-title {
font-size: 20px;
}
&__article-title {
@extend %__article-title;
&--red {
@extend %__article-title;
color: red;
}
}
&__article-subtitle {
font-size: 16px;
}
}
当增加gren
, blue
等其他颜色时, 代码依然可以保持简洁, 如下
.article-list {
width: 100%;
%__article-title {
font-size: 20px;
}
&__article-title {
@extend %__article-title;
&--red {
@extend %__article-title;
color: red;
}
&--green {
@extend %__article-title;
color: green;
}
&--blue {
@extend %__article-title;
color: blue;
}
}
&__article-subtitle {
font-size: 16px;
}
}
HTML
HTML需要注意的点如下
- block中嵌套的子block也可以做为block使用
- 没有block时, element不能直接使用
- 用简单类来描述
block中嵌套的子block也可以做为block使用
<div class="category-list">
<div class="category-list__category-title">
分类1
</div>
<div class="article-list">
<div class="article-list__article-title">
标题
</div>
<div class="article-list__article-title">
标题
</div>
</div>
<div class="category-list__category-title">
分类2
</div>
<div class="article-list">
<div class="article-list__article-title">
标题
</div>
<div class="article-list__article-title">
标题
</div>
</div>
</div>
在上述HTML代码中,block article-list
作为 block category-list
的子block, 这样使用完全没有问题。
由于1个block对应1个SCSS文件, article-list
和category-list
在定义样式时就可以完全不相互嵌套而保持独立。
没有block时, element不能直接使用
假设有SCSS代码如下
.hoge-block {
width: 5000px;
&__link {
color: blue;
cursor: pointer;
text-decoration: underline;
}
}
这时, 如果在不是hoge-block
的子节点位置使用hoge-block__link
的话,则违反了规则
<div class="article-list">
<!- 这里class名应该为hoge-block而不是article-list! ->
<a class="hoge-block__link">
跳转链接
</a>
</div>
作为BEM优点之一,就是容易预测SCSS里所定义的样式所影响的HTML哪些范围。如果采用上述写法, 将会使代码丧失这个优点, 也间接导致了维护hoge-block__link
的成本增加。所以 hoge-block__link
使用时,必须在hoge-block
之内。
用简单类来描述
如果使用多个类来指定hoge-block
, 也是违反了规则。如下
<div class="article-list hoge-block">
<a class="hoge-block__link">
跳转链接
</a>
</div>
正确的写法如下
<div class="hoge-block">
<div class="article-list">
<a class="hoge-block__link">
跳转链接
</a>
</div>
</div>
hoge-block
的内部使用hoge-block__link
并不违反规则- 但是,没有解决
hoge-block__link
出现在article-list
的内部 - 从长远来看, 这也会让SCSS维护成本变高
知识点
- block单位和大小比较自由
- 不要使用block的modifier
- 不要指定block的margin
- 只有block和element的多类名是允许的
- 通过block属性将公共样式提取到专用文件中
block单位和大小比较自由
对于一个BEM新手来说, 从哪里开始到结束来定义一个block是个经常被问到的问题。
block的大小一般由以下2个判断基准来决定
- 不止是1个页面, 多个地方使用到的UI元素, 则把它定义成block
- 当上面基准不满足的情况下,为了保持1个block不会太大而粗略地将它定义成block
只要遵循着前面的规则来设计, 一个大的block可以很容易地拆分成2个小的block. 可以基于是否可重复利用的部分
重构block
不要使用block的modifier
虽然BEM设计原则中,提到可以使用block的modifier。 但在SCSS中,如果使用block的modifier, 需要考虑的量就变得很大, 也会导致SCSS变得复杂化。
基于考虑block的modifier并不推荐使用
不要指定block的margin
假如有个用户页面包括朋友列表, 照片列表, 文章列表。代码如下
<div class="user-page">
<div class="friend-list">
<!- 省略 ->
</div>
<div class="image-list">
<!- 省略 ->
</div>
<div class="post-list">
<!- 省略略 ->
</div>
</div>
对应的CSS为
.user-page {
}
.friend-list {
margin-bottom: 10px;
}
.image-list {
margin-bottom: 10px;
}
.post-list {
margin-bottom: 10px;
}
在前面提到的定义block基准中有不止是1个页面, 多个地方使用到的UI元素, 则把它定义成block
这么一条。
block friend-list
中的margin-bottom: 10px;
是以用户页面为前提,当在用户页面以外调用此样式名称时,可能就会产生排版样式问题。
所以在block里指定margin
时,会使block的通用性降低
只有block和element的多类名是允许的
为了解决上面问题, 可以改写HTML代码如下
<div class="user-page">
<div class="user-page__list friend-list">
<!- 略 ->
</div>
<div class="user-page__list image-list">
<!- 略 ->
</div>
<div class="user-page__list post-list">
<!- 略 ->
</div>
</div>
样式代码如下
.user-page {
&__list {
margin-bottom: 10px;
}
}
.friend-list {
}
.image-list {
}
.post-list {
}
通过这种写法,可以保持各个block的通用性,也实现了margin.
通过block属性将公共样式提取到专用文件中
当想统一定义网站所有链接样式,如果在各个block, element中复制样式,会导致代码变得难以维护。
这个场合,通过placeholder selector
将链接的样式提前定义,各个block使用时再import进来,通过extend来实现。
- global.scss
%link {
cursor: pointer;
text-decoration: underline;
}
- article-list.scss
@import "global";
.article-list {
&__link {
@extend %link;
font-size: 16px;
}
}