避免以视觉效果来命名类
尽量避免以类的视觉效果来命名:如.half
。因为如果使用响应式布局,你的页面会有多种布局来适应多种显示设备的需求。一个元素可能要在台式电脑上占据一半的宽度,但在移动设备上你可能需要它占据全部的宽度。.half
的语义却把这个元素的宽度定死了:
<div class="row">
<div class="column half">
<!-- Your Content -->
</div>
<div class="column half">
<!-- Your Content -->
</div>
</div>
如果这样命名,那么在移动设备上你就需要用js删除.half
类然后增加.full
类!
更好的方式是:类名仅描述类的功能,对于那些需要响应式变化的类,我们使用媒体查询,让这个类在不同的环境中做出不同的布局表现。
@media (min-width: 500px) {
类名 {
// 窄显示设备下的样式
}
}
@media (max-width: 500px) {
类名 {
// 宽显示设备下的样式
}
}
以类的功能来命名
类名应该反映这个类的目的和类之间的层次结构,而不是布局表现。
BEM 命名方式
BEM 是Block Element Modifier的缩写,它将一个类名分成这三个部分:模块、模块元素、描述符。模块规定整个组件的样式;模块元素规定这个组件中子元素的样式;描述符描述一种特殊的种类,给模块或模块元素增加特别的样式,以表示这个元素处于某种特别的状态(比如说被选中的状态)。
使用BEM的类名清楚地展示了这个类的作用和层次结构关系,并且有效避免了类名重复导致样式覆盖。
BEM的写法
使用__
连接模块与模块元素,使用--
来连接描述符,如果一个名字由多个单词组成,用单词分隔符-
来分割。
// Block (Highest level)
.block {...}
// Element (Descendent of block)
.block__element {...}
// Modifier (Different state or version)
.block--modifier {...}
.block__element--modifier {...}
BEM例子
.person {}
.person__hand {}
.person--female {}
.person--female__hand {}
.person__hand--left {}
看到这个样式表,我们马上可以知道
-
person
就是整个部件; -
person__hand
描述person
的手(元素); -
person--female
描述person
中的女性(一个种类); -
person--female__hand
描述person--female
的手(元素); -
person__hand--left
描述person__hand
的一个种类:左手。
如果我们不使用BEM:
.person {}
.hand {}
.female {}
.female-hand {}
.left-hand {}
- 类与类之间的结构不清晰(比如,female到底是放在person的子标签中,还是放在同一个标签中?如果使用BEM,带描述符的类与不带描述符的类就一定放在同一个标签中,见下面的例子)
- 类名容易重复(比如female可以指不同的动物,如果出现了重名、样式覆盖,会导致动物的样式应用到人身上!)
另一个例子:
<div class="shopping-cart">
<div class="shopping-cart__item">
<!-- Your Content -->
</div>
<div class="shopping-cart__item shopping-cart__item--selected">
<!-- Your Content -->
</div>
</div>
以上例子描述了一个购物车组件的组成:
shopping-cart
规定了购物车组件的总体样式;组件中有元素shopping-cart__item
,规定了购物车中的商品条目的样式;shopping-cart__item--selected
则表示这是一个特别的商品条目(被用户选中的条目),我们可以给它添加特别的样式,比如红色的边框。
用了这种方式,我们看到类名就可以知道它规定了谁的样式,并且类与类之间的关系也清晰了。
再看一个例子:搜索栏组件
反例:
<form class="site-search full">
<input type="text" class="field">
<input type="Submit" value ="Search" class="button">
</form>
使用BEM以后:
<form class="site-search site-search--full">
<input type="text" class="site-search__field">
<input type="Submit" value ="Search" class="site-search__button">
</form>
这里的
site-search--full
其实犯了我们开始说的错误:用视觉效果来给类命名,这里应该使用媒体查询而不是用一个类来让搜索栏占满宽度。这个例子仅用来学习使用BEM。
什么时候不必使用BEM
- 有一些样式是在任何组件中都通用的:
.caps { text-transform: uppercase; } // 字母大写显示
- 有一些元素只是碰巧在这个组件中,与这个组件没有什么必然的联系,这个元素可以出现在很多其他的组件,比如:
我们只需要定义一次网站logo的样式:
.site-logo {}
网站logo可以放在网站的header,footer,任何地方,他不应该作为某一个组件的子元素:
.header {}
.header__logo {}
logo和header没有必然联系,他只是恰好出现在了header中,我们完全可以使用之前定义的.site-logo
。假如我们还要在footer中假如logo,难道我们还要定义一个footer__logo
吗?这太多余了!
因此我们在使用BEM的时候要考虑清楚:这个元素是这个模块的组成部分吗?还是说这个元素只是恰好出现在这个模块中?如果是后者,不必使用BEM。
参考文章:
http://adamkaplan.me/blog/grid-retrospective
https://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/
补充:对照真实的项目来理解
如果没有项目的体验,确实很难理解BEM的意义。BEM其实就是将类名分成三个部分来描述这个类的作用。微信的样式库也是这么做的,可以对照看看:https://github.com/weui/weui/blob/master/dist/style/weui.css
只用看其中和weui-cell
有关的类名就可以了。注意微信的样式库对连接符的使用和我这篇文章讲的有点不一样,一个下划线_
后面是描述符,两个下划线__
后面的是元素。
感兴趣的话还可以看看他们的DEMO,是如何使用的。
后续我会写更多关于响应式布局的内容。