命名规范/stylelint

785 阅读11分钟

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类名:

  1. 块名:所属组件的名称。
  2. 元素:元素在块里面的名称。
  3. 修饰符:任何与块或元素相关联的修饰符。

元素名加在双下划线之后(例如toggle__details),修饰符加在双横杠之后(如toggle__details--active)。

2. 编码规范

CSS,包括Less和Sass等预处理语言,是前端代码中的3大组成部分之一,其书写简便,语法宽松,但也常常伴随着一些错误,对其规范的价值和重要性不言而喻。另外只要代码质量是可以被维护的,就能很好的被工具混淆、压缩和合并。

定义适用于CSS/Less/Sass文件的编写格式和风格规则,目的是提高合作和代码质量,并使其支持基础架构。并进一步使用stylelint来保障规范的执行(和eslint校验类似,stylelint也有一个配置文件.stylelintrc.js)。

2.1 编码

  1. 用不带BOM头的UTF-8编码。
  2. 换行符统一使用LF
  3. 使用有效的CSS代码,避免使用CSS “hacks”。使用有效的CSS是重要的质量衡量标准,如果发现有的CSS代码没有任何效果的可以删除,确保CSS用法适当。

2.2 命名

优先使用简短、功能性或通用的名字,方便理解,会减少不必要的文档或模板修改。具体规则如下:

  1. 格式严格遵循bem命名规范,元素名加在双下划线之后(例如toggle__details),修饰符加在双横杠之后(如toggle__details--active),多个单词组成的块名、元素、修饰符中间用中横杠连接;
  2. 仅使用小写字母,包括选择器、属性、属性值(content属性的字符串属性值例外);
  3. 仅使用类名选择器,并且取功能性/通用且有意义的名字(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 固定代码顺序

  1. dollar-variables/at-variables,Sass/Less变量声明
  2. Custom Properties
  3. at-rules
  4. declarations
  5. supports at-rule
  6. media at-rule
  7. rules

2.4.8 固定声明顺序

CSS不同类型属性的数据如下,具体属性顺序参看下面配置。

  1. Content
  2. Positioning
  3. Display & Flex/Grid
  4. Box Model
  5. Border
  6. Background
  7. Font & Text
  8. Transition & Transform & Animation
  9. Other

2.5 注释

  1. 适当合理用注释来解释代码:它包括是什么,它的目的是什么,它能做什么,为什么使用这个解决方案?

  2. 按组写注释,按照功能的类别来对一组样式表写统一注释。独立成行。

  3. 只用TODO来强调代办事项,不要用其他的常见格式,例如@@

    1. 可在括号里面附加联系人(用户名或电子邮件列表),例如TODO(contact)
    2. 可在冒号之后附加活动条目说明等,例如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份规范的最佳实践)基础上进行扩展。默认severityerror

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 */给删除。