优质的组件库都离不开BEM!
1. 摘要
当你在编写css代码的时候,是否遇到这样的困扰: 不知道取什么class名? 修改某个组件的样式,担心影响了其他组件? 编写的组件样式如何复用?为了解决这些问题,聪明的程序猿发明了BEM命名法。
BEM命名法,是对css命名的一种规范,将页面模块化,隔离样式,提高代码的复用性,减少后期的维护成本。BEM的意思就是Block(块)、Element(元素)、modifier(修饰符),通过双下划线 __
或者双中划 --
链接。
BEM通常用于框架开发中,比如微信WEUI、饿了么element-ui、有赞vant等。笔者也是通过阅读这些优秀框架的源码,学习到了这一套css命名大法,从此走上人生巅峰,赢取白富美。
2. 为什么需要BEM命名法
2.1 样式隔离,避免css样式污染
css样式污染的根本原因,是因为css没有作用域。BEM通过特殊的命名方式,给css创造一个“作用域”,就能有效避免css样式全局污染。例如,给输入框命名:
// 普通
.base input {}
// BEM命名法
.base-input__inner {}
普通的命名法, 会作用于所有 class='base'
的后代元素。本来你只想给当前元素加样式,结果不小心影响了其他元素,这就是样式污染。
BEM命名法,只会作用于 class='base-input__inner'
的元素, 达到样式隔离。 不会影响其他元素。
有人会说 css-module
, 也能实现css“作用域”,而且作用域更唯一,我为什么要用BEM ?
2.2 代码更易覆盖
假如,你正在开发一个通用的输入框组件,用了 css-module
。打包后是这个样子:
.base-input-sdFh3sxLwo5uer {}
另一个人用了你的输入框组件,但是觉得样式不好看,想修改样式。试了半天,发现,根本无法用css精确选择你的组件,因为 .base-input
后面的hash值是动态的。于是,他捶胸顿足,发誓再也不用你的组件了。
2.3 代码更易读
还是刚刚的输入框组件,base-input__inner
,不需要我解释,你一眼就能看出:base
代表基础组件,input
代表输入框组件, inner
是组件中的某一块。
3. 什么是BEM命名法
BEM其实是块(block)、元素(element)、修饰符(modifier)的缩写,利用不同的区块,功能以及样式来给元素命名。这三个部分使用 __
与 --
连接(这里用两个而不是一个是为了留下用于块儿的命名)。命名约定的模式如下:
.block{}
.block__element{}
.block--modifier{}
block
代表更高级别的抽象或组件block__element
代表block
的后代,用于形成一个完整的block
的整体block--modifier
代表block
的不同状态或不同版本
3.1 常用规范
-
block
、element
、modifier
包含多个单词时,用一个中划线-链接,如:.el-dropdown-menu .el-button
-
block
和element
用双下划线__
链接,例://表单项 .form__item //导航项 .menu__item
-
element
和modifier
用双中划线--
链接,如表示按钮的不同状态://默认 .el-button--default //成功 .el-button--success
-
用js控制样式时,css命名用
is-
开头,如is-success
、is-failed
、is-disabled
3.2 常用的元素名
-
表单元素:
form
、form-item
、input
、select
、radio
、checkbox
、switch
、rate
、datePicker
-
导航元素:
nav
、subnav
、menu
、tab
-
提示:
alert
、message
、messageBox
、notification
-
数据展示:
table
process
tree
pagiantion
-
其他:
button
、icon
4. 如何用好BEM命名法
4.1 页面命名
用 page-
开头,page
表示这是一个页面, 而不是组件。
给页面命名时,BEM可以搭配 css-module一
起使用。既能保证打包后选择器的唯一,又容易调试。例如
// 编译前
.page-index {}
.page-zufang {}
// 编译后
.page-index-70yGFBg1eKjbSIwN {}
.page-zufang-mFTy62A1t83zjDbh {}
使用css-module,打包后的clss名是可以修改的参考
# 让打包后的文件更容易识别
{
test: /\.css$/,
use: [
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[local]--[hash:base64:5]'
}
}
]
}
页面中的选择器,都嵌套在页面根选择器内(.page-xxx),保证所有样式,只作用于当前页面。例如
<!- 页面命名 page-home ->
<div class="page-home">
<div class="the-form">
<div class="the-form-item">
<div class="the-input"></div>
</div>
</div>
<div class="the-table">
<div class="the-table-content"></div>
</div>
</div>
<style>
.page-home {
.the-form {}
.the-form-item {}
.the-input {}
.the-table {}
.the-table-content {}
}
</style>
4.2 公共组件命名
用 base-
开头,base
表示公共组件:
<div class="base-input">
<input class="base-input__inner"/>
</div>
// 选择器避免嵌套,降低选择器权重
.base-input {}
.base-input__inner {}
公共组件的每一个class名,带上组件的作用域前缀,如 base-input__inner
的作用域前缀是 base-input
。
选择器不宜嵌套,让选择器的权重尽可能低。原因如下:
base-input__inner
已经具有有作用域了,无需再嵌套。- 由于组件选择器权重较低,在组件外修改组件样式时,覆盖样式非常方便。
4.3 局部组件命名
用 the-
开头,the
表示某一特定的组件。
<div class="the-header">
<div class="the-header__title" />
<div class="the-header__desc">
</div>
// 选择器避免嵌套,降低选择器权重
.the-header {}
.the-header__title {}
.the-header__desc {}
局部组件的每一个class名,带上组件的作用域前缀,如 the-header__title
的作用域前缀是 the-header
。
局部组件,也不宜嵌套, 降低选择器权重。
局部组件也可以搭配 css-module
一起用,因为局部组件只给少数特定页面使用,修改样式,可以在组件内部直接修改。
5. 其他注意事项
5.1 命名语义化
怎样衡量你的命名是语义化的?让一个人新人,来看一下你的代码,不需要解释,就能知道这个类的作用,就比较语义化。
通常,可以根据模块的功能而命名,如页面头部header、导航栏nav、主体main、侧边栏sidebar、底部footer等,这样整个页面看起来就比较清晰了,维护起来也比较方便。
// bad
.fl { ... }
.fr { ... }
// good
// 左浮动
.is-float-left { ... }
// 右浮动
.is-float-right { ... }
上面的代码,fl
、fr
之类的命名,表达意思不够清晰,要知道具体的含义,还得去看代码,而 is-float-left
,就表达得非常清晰。
5.2 使用 class(类) 选择器,避免使用 id、标签、伪类选择器
标签、伪类 等选择器范围太广,不具有“作用域”的作用,会污染全局样式。例如,下面的代码中,.the-header a
选择器会选中 the-header
所有后代元素 <a></a>
<div class="the-header">
<a></a>
</div>
// bad
.the-header a { ... }
5.3 覆盖第三方组件样式时,重新起一个class名
使用原来的class名修改样式,可能会不小心影响了后代组件的样式。为了消除这个隐患,可以重新起一个class名,只作用于当前组件。例如修改ant-design的输入框组件样式
<div className="the-form">
<Input className="the-input" />
</div>
// bad 会影响the-form 后代的所有输入框
.the-form .ant-input {}
// good 只会只用于className='the-input'的输入框
.the-form .the-input{}
6. 参考
CSS BEM 书写规范
CSS命名规范-BEM
本文转自:BEM命名法