1. 什么是BEM命名规范
官方网址:getbem.com/naming/
BEM 由 Yandex 团队提出的一种前端 CSS 命名方法论。BEM 是 BlockElementModifier
的缩写 ,其中B 表示块(block)、E 表示元素(element)、M 表示修饰符(modifier)。
这三个部分通常使用__
与--
连接。即: .块__元素--修饰符{}
Block
:代表了一个独立的块级元素,可以理解为功能组件块。一个 Block 就是一个独立的区块,比如头部是个 block,表单功能输入框是一个block,block可大可小。Element
:是 Block 的一部分不能独立来使用的,所有的 Element 都是与 Block 紧密关联的。例如一个带有icon
的输入框,那么 这个icon
就是这个输入框 Block 的一个 Element,脱离了输入框的 Block 那么这个 icon 就没有意义。Modifier
:是用来修饰 Block 或 Element,表示 block 或者 element 在外观或行为上的改变。例如,上面提到的输入框 Block,当鼠标悬停时边框高亮,那么这个高亮的效果就应该用 Modifier 来实现。
<div class="toggle toggle—simple">
<div class="toggle__control toggle__control--active">
<h1 class="toggle__title">Title 1</h1>
</div>
<div class="toggle__details toggle__details--active">…</div>
...
</div>
BEM只是一个CSS类名命名规则,让每一个CSS类名具备详细的自描述性,不涉及如何书写CSS结构,只是建议每个元素都添加带有如下内容的CSS类名:
- 块名:所属组件的名称。
- 元素:元素在块里面的名称。
- 修饰符:任何与块或元素相关联的修饰符。
元素名加在双下划线之后(例如toggle__details
),修饰符加在双横杠之后(如toggle__details--active
)。
2. 编码规范
CSS,包括Less和Sass等预处理语言,是前端代码中的3大组成部分之一,其书写简便,语法宽松,但也常常伴随着一些错误,对其规范的价值和重要性不言而喻。另外只要代码质量是可以被维护的,就能很好的被工具混淆、压缩和合并。
定义适用于CSS/Less/Sass文件的编写格式和风格规则,目的是提高合作和代码质量,并使其支持基础架构。并进一步使用stylelint来保障规范的执行(和eslint校验类似,stylelint也有一个配置文件.stylelintrc.js
)。
2.1 编码
- 用不带BOM头的UTF-8编码。
- 换行符统一使用
LF
。 - 使用有效的CSS代码,避免使用CSS “hacks”。使用有效的CSS是重要的质量衡量标准,如果发现有的CSS代码没有任何效果的可以删除,确保CSS用法适当。
2.2 命名
优先使用简短、功能性或通用的名字,方便理解,会减少不必要的文档或模板修改。具体规则如下:
- 格式严格遵循bem命名规范,元素名加在双下划线之后(例如
toggle__details
),修饰符加在双横杠之后(如toggle__details--active
),多个单词组成的块名、元素、修饰符中间用中横杠连接; - 仅使用小写字母,包括选择器、属性、属性值(
content
属性的字符串属性值例外); - 仅使用类名选择器,并且取功能性/通用且有意义的名字(CSS类名取决于适用在哪些地方,而不是这些样式本身),禁止标签名和ID选择器(在Vue中建议使用CSS Modules,不建议使用Scoped CSS);
2.3 排版
2.3.1 缩进
缩进所有代码块(“{}”之间)内容,每次缩进2个空格,不要用TAB键或多个空格来进行缩进,提高代码层次结构的清晰度。
/* 不推荐 */
.example {
color: blue;
}
@media screen, projection {
html {
background: #ffffff;
color: #ffffff;
}
}
/* 推荐 */
.example {
color: blue;
}
@media screen, projection {
html {
background: #ffffff;
color: #444444;
}
}
2.3.2 声明完结
所有声明都要用“;”结尾。考虑到一致性和拓展性,请在每个声明尾部都加上分号。
/* 不推荐 */
.test {
display: block;
height: 100px
}
/* 推荐 */
.test {
display: block;
height: 100px;
}
2.3.3 属性名完结
在属性名冒号结束后加一个空字符。出于一致性的原因,在属性名和值之间加一个空格。
/* 不推荐 */
h3 {
font-weight:bold;
}
/* 推荐 */
h3 {
font-weight: bold;
}
2.3.4 选择器和声明分行
将选择器和声明隔行。每个选择器和声明都要独立新行。
/* 不推荐 */
a:focus, a:active {
position: relative; top: 1px;
}
/* 推荐 */
h1,
h2,
h3 {
font-weight: normal;
line-height: 1.2;
}
2.3.5 规则分行
每个规则独立一行。两个规则之间隔行。
html {
background: #fff;
}
body {
margin: auto;
width: 50%;
}
2.3.6 删除行尾白空格
删除行尾白空格。行尾空格没必要存在。
2.4 属性/属性值
2.4.1 尽量使用缩写
写属性和属性值的时候尽量使用缩写,甚至只设置一个值。很多CSS属性都支持缩写shorthand,例如font
。使用缩写可以提高代码的效率,方便理解。
/* 不推荐 */
padding-top: 0;
padding-bottom: 2em;
padding-left: 1em;
padding-right: 1em;
font-family: palatino, georgia, serif;
font-size: 100%;
line-height: 1.6;
border-top-style: none;
/* 推荐 */
border-top: 0;
font: 100%/1.6 palatino, georgia, serif;
padding: 0 1em 2em;
2.4.2 省略0后面的单位
省略0后面的单位。非必要的情况下0
后面不用加单位。
/* 不推荐 */
margin: 0px;
padding: 0px;
/* 推荐 */
margin: 0;
padding: 0;
2.4.3 不要省略小数点前面的0
不要0开头小数点前面的0。保持代码的可读性,压缩的工作交给工具。
/* 不推荐 */
font-size: .8em;
/* 推荐 */
font-size: 0.8em;
2.4.4 省略URI外的引号
省略URI外的引号。不要在url()
里用 (""
,''
) 。
/* 不推荐 */
@import url("//www.google.com/css/go.css");
/* 推荐 */
@import url(//www.google.com/css/go.css);
2.4.5 十六进制使用6个字符
颜色值中的十六进制使用6个字符。保持代码的可读性,压缩的工作交给工具。
/* 不推荐 */
color: #ebc;
/* 推荐 */
color: #eebbcc;
2.4.6 省略厂商前缀
省略厂商前缀,保持代码的可读性,通过PostCSS之类工具的自动补全完成。
2.4.7 固定代码顺序
- dollar-variables/at-variables,Sass/Less变量声明
- Custom Properties
- at-rules
- declarations
- supports at-rule
- media at-rule
- rules
2.4.8 固定声明顺序
CSS不同类型属性的数据如下,具体属性顺序参看下面配置。
- Content
- Positioning
- Display & Flex/Grid
- Box Model
- Border
- Background
- Font & Text
- Transition & Transform & Animation
- Other
2.5 注释
-
适当合理用注释来解释代码:它包括是什么,它的目的是什么,它能做什么,为什么使用这个解决方案?
-
按组写注释,按照功能的类别来对一组样式表写统一注释。独立成行。
-
只用
TODO
来强调代办事项,不要用其他的常见格式,例如@@
。- 可在括号里面附加联系人(用户名或电子邮件列表),例如
TODO(contact)
。 - 可在冒号之后附加活动条目说明等,例如
TODO: 活动条目说明
。
- 可在括号里面附加联系人(用户名或电子邮件列表),例如
/* Header */
.adw-header {
}
/* Footer */
.adw-footer {
}
/* Gallery */
.adw-gallery {
}
/* TODO(cha.jn): 重新置中 */
.example {
}
/* TODO: 删除可选元素 */
.example {
}
2.6 其他
2.6.1 协议
省略嵌入式资源协议声明,即省略图像、媒体文件、样式表和脚本等URL协议头部声明http:
和https:
,如果不是这两个声明的URL则不省略。
省略协议声明,使URL成相对地址,防止内容混用问题(Mixed Content),避免小文件重复下载。
/* 不推荐 */
.example {
background: url(http://www.google.com/images/example);
}
/* 推荐 */
.example {
background: url(//www.google.com/images/example);
}
3. Linter工具
编码规范从语言正确性、语言特性和语言风格三方面定义了代码的规范性,借助Linter工具可以进一步保障规范的执行,自动识别非法颜色值、Calc错误参数、单位错误、属性拼写错误等。在StyleLint中,有170+条相关内置规则可以在rules中进行配置,完整覆盖上面的规范。
在实际项目中,可以在官方预定义的规则集stylelint-config-standard(已经覆盖了Idiomatic CSS Principles,Google's CSS Style Guide,Airbnb's Styleguide 和 @mdo 的 Code Guide 4份规范的最佳实践)基础上进行扩展。默认severity
为error
。
3.1. 安装依赖
npm install stylelint --save-dev
npm install stylelint-config-standard --save-dev
npm install stylelint-scss --save-dev
npm install stylelint-selector-bem-pattern --save-dev
npm install stylelint-order --save-dev
3.2 工具配置
stylelint的配置文件是.stylelintrc.js
,可以根据需要进一步添加建议配置,配置详见startup-vue3-app/generator/template/_stylelintrc.js。
module.exports = {
extends: 'stylelint-config-standard',
plugins: [
'stylelint-scss',
'stylelint-selector-bem-pattern',
'stylelint-order',
],
rules: {
'plugin/selector-bem-pattern': {
// 指定Preset Patterns,支持suit和bem两种(无默认值)
preset: 'bem',
componentSelectors: {
initial: '^\.{componentName}(?:__[-a-z]+)?(?:--[a-z]+)?$',
combined: '^\.{componentName}(?:__[-a-z]+)(?:--[a-z]+)?$',
},
utilitySelectors: '^\.util-[a-z]+$',
ignoreSelectors: ['^\.icon-'],
ignoreCustomProperties: [],
implicitComponents: true
},
'order/order': [
[
'dollar-variables',
'at-variables',
'custom-properties',
'at-rules',
'declarations',
{
type: 'at-rule',
name: 'supports',
},
{
type: 'at-rule',
name: 'media',
},
'rules',
],
{
severity: 'warning',
disableFix: true,
},
],
'order/properties-order': [
[
// 详见startup-vue3-app/generator/template/_stylelintrc.js
// Content
...
// Positioning
...
// Display & Flex/Grid
...
// Box Model
...
// Border
...
// Background
...
// Font & Text
...
// Transition & Transform & Animation
...
// Other
...
],
{
unspecified: 'bottom',
severity: 'warning',
},
],
},
};
3.3 忽略配置
stylelint自动忽略node_modules和bower_modules,在项目中,添加.stylelintignore来设置校验规则忽略的文件。
*.js
*.png
*.eot
*.ttf
*.woff
3.4 命令配置
在package.json中,集成scripts命令的脚本如下
"scripts": {
"lint:style": "stylelint src/**/*.{css,scss,sass,less,vue}"
}
3.5 自动修复
在命令行中添加--fix
,在可能的情况下(有些情况下无法进行修复),则可以自动修复不满足规则的css代码。需要注意的是:手工执行自动修复,以确保变更是符合预期的,切忌大范围的不能Review的自动修复。
对于具有标准语法的CSS,stylelint使用postcss-safe-parser修复语法错误。
"scripts": {
"lint:style": "stylelint src/**/*.{css,scss,sass,less,vue} --fix"
}
3.6 Vue-Cli集成
详细参考Vue Loader 中 StyleLint的介绍,配置参考Vue Cli webpack 相关,主要配置如下。
1. 安装依赖
npm install -D stylelint-webpack-plugin
2. 添加配置
const StyleLintPlugin = require('stylelint-webpack-plugin');
module.exports = {
configureWebpack: {
plugins: [
new StyleLintPlugin({
files: ['src/**/*.{vue,htm,html,css,sss,less,scss,sass}'],
}),
],
},
};
4. 再进一步
4.1 质量保障全生产链条覆盖
@startuml
开发校验 -> 开发校验: 实时在编辑器中提供校验信息,即时完成错误修复
开发校验 --> 提交校验
提交校验 -> 提交校验: 提交时自动执行校验,拒绝不合规代码提交
提交校验 --> 合并校验
合并校验 -> 合并校验: 合并时自动执行校验,拒绝不合规代码合并
合并校验 --> 定时校验
定时校验 -> 定时校验: 定时任务独立执行校验,把不合规代码及时报警
@enduml
序号 | 环节 | 修复成本 | 说明 |
---|---|---|---|
1 | 开发环节 | 低 | 代码编写时马上修改,第一次即生产高质量代码 |
2 | 提交环节 | 中 | 代码提交时尽早修改,不让低质量代码流出本机 |
3 | 合并环节 | 高 | 代码合并时关键修改,不让低质量代码流入仓库 |
4 | 定时环节 | 最高 | 代码合并后独立把关,代码质量报警和救赎 |
4.2 开发环节校验——vscode-stylelint
vscode-stylelint是VS Code的stylelint插件,可以提供编码时的实时校验和自动修复能力。
在.vscode/settings.json
或者设置选项中添加以下信息,会启动stylelint插件的自动修复功能,在保存当前文件时,会由stylelint自动修复代码。但千万注意确保变更是符合预期的,切忌大范围的不能Review的自动修复。
{
"editor.codeActionsOnSave": {
"source.fixAll.stylelint": true
}
}
4.3 提交环节校验——husky & lint-staged
husky结合lint-staged可以在本地提交commit时进行相关校验。
pre-commit
钩子由git commit
触发,在进行提交之前被调用,但可以使用--no-verify
选项绕过。它不接受任何参数,脚本中退出非零状态会在创建提交之前中止git commit
命令。
1. 安装husky和lint-staged
npm install husky --save-dev
npm install lint-staged --save-dev
2. 配置package.json
{
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{ts,tsx,js,jsx}": [
"npm run lint"
],
"*.{css,scss,sass,less}": [
"npm run lint:style"
]
}
}
这里需要注意一点,lint-staged
中会对匹配的文件都执行后面的命令,所以key
值不能为*
而是*.{css,scss,sass,less}
,不然当匹配到a.json
时,会执行命令npm run lint:style a.json
对json进行校验。当然,如果一点需要使用*
,可以考虑在.stylelintignore
中进行设置。
3. 结合Vue-Cli使用
youkiefork自husky,做了一些定制化的改动 ,使得钩子能从package.json的 "gitHooks"属性中读取,本着减少工具的原则,如果直接用vue的脚手架就可以不用安装husky。具体使用方法如下,和husky基本一致。
安装husky和lint-staged
npm install lint-staged --save-dev
配置package.json
{
"gitHooks": {
"pre-commit": "lint-staged"
},
"lint-staged": {
"*.{ts,vue}": [
"npm run lint"
],
"*.{css,scss,sass,less,vue}": [
"npm run lint:style"
]
}
}
4.4 合并环节校验——CI校验
pre-commit
钩子可以通过--no-verify
绕过,所以我们在gitlab中还需要添加pipelines用于自动校验,并将校验结果及时反馈。
Todo: 待整合到template中。
4.5 定时环节校验——Echo/Sonar校验
Todo: 细化Echo/Sonar校验细化规则规则。
5. 常见问题
- css中存在
/*# sourceMappingURL=defaultTheme.css.map */
,且该文件中存在css校验不通过的内容,那么在调用stylelint --fix
时,会把/*# sourceMappingURL=defaultTheme.css.map */
给删除。