✨✨相信稍微对代码有些追求的小伙伴,从HTML、CSS、JS,再到Git等等,都会尽量的规范自己的代码。如果不止自己一个前端开发,相信大家都会遵循一套代码开发规范(自己制定的也好,遵循行业内比较通用的规范也罢)。
人非圣贤,即使再自律的小伙伴,也难免会犯错误,或者说一不小心就在某些地方忘记了规范的代码书写。那么怎么办呢?如果是流程比较完成的小Team,可能会有代码review,嗯!这是一个非常好的习惯,可以提高大家代码质量减少可能的错误等等。但是,如果是一些比较基础的像代码格式,书写顺序等等这些错误,要放在review阶段来做吗?或者说,team中好些个小伙伴,你作为小leader能review的过来这些错误吗?
既然有了需求,那就要有对应的解决方案才行!!!而这些前端代码规范相关的内容,如果交给工具去做,那不就是省去了很多力气了吗,而且更准确、更高效,不是吗?说到这里,前端代码规范要做哪些事情呢?来,看下面这张图:
🌟🌟 git-hooks工具
工欲善其事,必先利其器。Git的hooks钩子函数可以让我们在执行git的一些命令之前或之后执行一些其他操作,我们后续很多的代码检查都会放在代码commit时或者push之前进行检查等。而husky(俗名哈士奇)就是一个git的hooks工具,可以让我们更方便的使用git hooks。
- 第一步,先给项目初始化git(如果没有被git初始化的话)
# 项目根目录下
git init
- 安装Husky
# 安装 (局部安装即可)
npm i husky -D
- 在package.json文件中配置husky
{
"husky": {
"hooks": {
"pre-commit": "npm test",
"pre-push": "npm test",
}
}
}
通过配置husky.hooks['pre-commit']
,则每次当我们执行git commit
之前都会先运行我们这里定义的命令(比如进行一些代码规范检查等等)。重点强调:
-
一定要==先git初始化项目==,然后才是安装
husky
依赖(安装husky之后它会自动往.git/hooks/
文件中添加很多钩子文件)。这就是为什么要先git后husky的原因。 -
如果先安装了
husky
,也可以删除依赖包,待初始化git之后再重新安装husky的依赖(最简单的做法就是删除node_modules文件夹 -> git init -> 安装项目依赖
)。 -
查看
husky
的钩子文件,通过查看.git/hooks/
里面的文件,可以看到自动添加了很多husky
的钩子文件,如下图:
husky
支持所有的git-hooks,常见的有:
名称 | 说明 | 绕过方法 |
---|---|---|
pre-commit | commit之前的钩子 | --no-verify参数 |
commit-msg | commit之前或merge之前的钩子 | --no-verify参数 |
post-commit | commit之后的钩子 | |
pre-push | push之前的钩子,可用于阻止push |
更多的钩子参考git钩子文档。
🌟🌟仅校验当前改动内容
俗话说,弱水三千只取一瓢饮
通过上面介绍,我们知道来可以通过husky来在commit时进行各种lint操作(具体的lint操作后面会一一讲解)。那么,问题来了,如果我们想只针对当前我们改动的内容进行lint,而不是每次都全部lint应该怎么办呢?具体原因后面会进行讲解。
lint-staged可以结合husky只针对当前提交到暂存区的内容(即git add的内容)进行前置的操作,比如eslint检查修复,prettier美化等等。👇下面看如何具体使用:
- 安装
lint-staged
cnpm i lint-staged -D
- 修改
husky
的钩子配置
比如,我们配置的是commit的前置钩子函数,在commit之前先会运行lint-staged
命令
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
- 配置
lint-staged
根目录下新建配置文件,也可以设置在package.json
:
# 根目录下新建配置文件(Mac),也可以手动创建
touch .lintstagedrc
写入配置,比如我们只对所有改动的src文件夹下的js文件进行lint-staged的操作,这里配置了eslint进行检查和自动修复一些格式,然后对lint-staged改动过的文件再add一次。配置文件中写入:
{
"./src/*.js": [
"eslint --fix"
]
}
这里就是配置,对于我们当前改动的所有js文件,执行后面的命令。配置对象的key就是配置的文件匹配规则,值则是一个对匹配到的文件执行的命令数组。这里我们不需要在"eslint --fix"
之后额外再添加git add的
操作了,因为lint-stage
会把所有的改动自动添加暂存区中。还可以进行一些美化的操作。扩展一下,如果同时需要针对多种文件类型,既可以增加配置项,也可以直接:
{
"./src/*.{js,jsx}": [
"eslint --fix",
"git add"
],
"./src/*.其他类型的文件匹配规则": []
}
lint-staged
最大的区别就是只针对当前改动的文件进行操作,而不是全部,这对于没有规范的老项目,需要维护进行引入eslint的时候是个福音,不然给老项目进行代码格式化可是个灾难。
🐢🐢Angular的Git提交信息规范
有了这些基础技能加持,我们可以开始前端规范相关的lint了。首先我们来说说如果规范Git的提交规范,Git工作流不再此次讨论范围内,这不属于本次lint工具的范围内,和大家团队的工作模式有关。
这里讨论一下如果限制Git的提交规范,首先要指定一下Git的提交规范,不然的一大堆无用信息的提交注释,是会让人抓狂的!!!这里推荐使用Angular的Git提交信息规范,在业界算是比较通用的,主要是相关的一些工具基本是可以满足一些常见需求的。这对于前端基建薄弱的小Team,应该是比较有帮助的。那么接下来花一点时间,介绍一下Angular的Git提交规范:
- 规范包含三个部分,
Header、Body、Footer
。
Header包含三部分, type是当前提交类型(必选),scope是当前提交涉及到的文件范围(可选),subject是当前提交的描述<type>(<scope>): <subject>
- type类型说明
type | 说明 |
---|---|
feat | 新的功能 |
fix | Bug的修复 |
docs | 仅改变说明文档 |
style | 代码格式的美化(formatting) |
refactor | 重构(没有feat和fix操作) |
perf | 性能优化 |
test | 增加单元测试、修改当前单元测试 |
build | 项目构建脚本或者外部依赖的改动(webpack、npm等) |
ci | CI/CD相关文件、脚本的改动(Travis, Circle, BrowserStack, SauceLabs) |
chore | 出src/test以外的其他改动(比如根目录下各种rc文件的改动等) |
revert | 版本回退等 |
# 举个例子
git commit -am "feat(TheNav): 完成导航组件的封装"
🌟🌟辅助生成git commit
信息的工具
有了提交规范,肯能有些小伙伴会记不住那么多的type类型导致在提交时犯了难。别着急,我们有解决办法,让你可以安安心心的懒。
commitizen用于辅助生成git commit信息规范的工具。如下图,使用效果:
是不是很Nice,内心是不是有那么一丝丝❤️心动❤️的感觉~~~来吧,看看如果使用:
- 安装
cnpm i commitizen -D
- 配置参数(package.json)
{
"scripts": {
"commit": "git-cz"
},
"config": {
"commitizen": {
"path": "cz-conventional-changelog"
}
},
}
config.commitizen.path
是配置commitizen的适配器的,我们在安装commitizen的时候自动安装了符合Angular规范的适配器cz-conventional-changelog
,不需要单独安装的,也可以安装其他适配器或者自定义commitizen的提示内容(自定义插件工具文档)的生成规则。
- 以后使用
npm run commit
代码git commit操作即可。
🌟🌟校验commit信息是否符合规范
即使有了自动生成的工具,我们还是得要在commit-msg时检查一下滴,不能放过落网之鱼滴~~~你不能保证小伙伴一定使用npm run commit
命令对吧,比如有些小伙伴就是喜欢使用git commit -am ""
来提交。这时候还是我们得针对用户的提交信息校验一次,不管他使用什么方式commit
的,commitlint就派上用场了!!!
- 安装
cnpm i @commitlint/{config-conventional,cli} -D
- 配合husky的hooks使用
{
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
}
- 配置文件
根目录下新建commitlint.config.js文件:
module.exports = {
extends: [
'@commitlint/config-conventional'
]
};
这样,在我们使用commit功能的时候就会进行commit信息的规范验证。如果不符合Angular规范的提交,就会自动提交失败。也可以自定义一些类型,具体的还是看文档吧!!!
🌟🌟自动生成CHANGELOG
我们可以看到很多库都有变更日志的,如果手写那岂不是类似宝宝了。我们是不是期望也有一款工具可以辅助我们完成这件事?
在我们的git提交规范是基于Angular的前提下,我们可以使用conventional-changelog-cli
来自动生成的,下面看看如何使用吧:
- 安装
cnpm i conventional-changelog-cli -D
- 配置script命令
我们定义一个用于生成changeLog的脚本命令,注意这里是每次都重新生成覆盖以前的。也可以设置每次都在文件前追加
"scripts": {
"version": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md"
},
详情可以查阅conventional-changelog-cli文档,最终效果如下图:
🌟🌟校验css规范
上面聊了一些本地开发时的一些git提交规范Lint相关的内容。接下来我们就介绍一下代码开发相关的一些规范来。那么首先,我们从css的规范Lint开始。就像JS的Lint一样,css我们也需要校验各种错误,比如像空格、分号的代码格式问题啊;禁止无效的颜色值啊;设置最大的嵌套深度啊;指定缩进、换行啊;属性值必须缩写啊等等。
stylelint是一个用于约束css规范的lint工具。可以配合css/less/scss/sass/stylus
等一起用,理论上post-css解析器支持的,都可以兼容使用。会自动根据文件类型推导需要使用的解析语法,当然了也可以直接指定解析的语言类型。👇下面看下如何使用吧:
- 安装stylelint和规则配置集合
cnpm i stylelint stylelint-config-standard -D
stylelint-config-standard
是stylelint
的一套代码规则集合,包含了常用的css规范约束,是官方推荐的一套基本的规则集合。而stylelint-config-standard
规范继承了stylelint-config-recommended
规范集合,在次基础上又开启和关闭的一些规则。
- 配置文件
在安装好来依赖之后,我们需在项目中进行配置。根目录下新建如下类型的文件都可以:
/**
* .stylelintrc.json or .stylelintrc
* 使用标准的json格式即可
* 注意,json文件是不可以写注释的,这里这是为了说明
* 当然了,json注释有一些取巧的方式,这里不做讨论
*/
{
"extends": "stylelint-config-standard"
}
/**
* .stylelintrc.js or stylelint.config.js
* 注意导出方式是cmd规范的,不是es的方式
*/
module.exports = {
plugins: [],
extends: 'stylelint-config-standard',
rules: {}
}
// 更多方式暂且不说了,常用这些的即可
// 个人更喜欢js的方式,语法写着比json舒服
extends
字段就是使用我们的安装的stylelint规则集合。当然了,也可以不使用这些集合规则,我们也完全可以自己定义一套。extends是
也可以是个数组(只有一个也可以是字符串),可以后面的会合并到前面,即同一个rule规则,后面的会覆盖前面的。数组每一项既可以是下载的包,也可以是本地的文件路径(符合require.resolve()
算法解析规则的路径都可以)。
- 配置文件的其他字段说明
rules
定义规则的集合,是一个对象,键是规则名称,值是规则配置:
// 例如:
module.exports = {
rules: {
"block-no-empty": null,
// 例如,定义缩进规则
'indentation': [
// 2个空格
2,
{
// 接收xxx
"except": ["block"],
// 错误的提示信息
"message": "Please use 2 spaces for indentation.",
// 规则级别,是警告,也可以是错误error
"severity": "warning"
}
]
}
}
- 内置规则
stylelint默认内置集成了170个规则(文档地址),但是默认都没有开启(需要自己指定,包括一些推荐的配置库,也是一一列举出来的)。列举一些常见的规则:
规则 | 说明 |
---|---|
color-no-invalid-hex | 禁止使用无效的十六进制颜色 |
unit-no-unknown | 禁止使用未知单位 |
property-no-unknown | 禁止未知属性 |
keyframe-declaration-no-important | keyframe中不允许使用!important |
declaration-block-no-duplicate-properties | 禁止在声明块中使用重复的属性 |
block-no-empty | 禁止空块 |
selector-pseudo-class-no-unknown | 禁止未知的伪类选择器 |
selector-pseudo-element-no-unknown | 禁止使用未知的伪元素选择器 |
selector-type-no-unknown | 禁止未知类型选择器 |
comment-no-empty | 禁止空注释 |
no-extra-semicolons | 禁止使用多余的分号 |
no-invalid-double-slash-comments | 禁止//...CSS不支持的双斜杠注释 |
color-named | 禁止命名的颜色 |
color-no-hex | 禁止使用十六进制颜色 |
length-zero-no-unit | 不允许长度为零的单位(可自动修复) |
color-hex-case | 指定十六进制颜色的小写或大写(可自动修复) |
shorthand-property-no-redundant-values | 属性值可以简写的需要简写 |
max-nesting-depth | 最大嵌套深度 |
number-leading-zero | 数字小于1时的前导0 |
number-no-trailing-zeros | 禁止尾随0 |
unit-case | 为单位指定小写或大写(可自动修复) |
string-quotes | 字符串指定单引号或双引号 |
value-keyword-case | 将关键字值指定为小写或大写 |
property-case | 为属性指定小写或大写 |
comment-whitespace-inside | 在注释标记的内部需要或不允许空格(可自动修复) |
comment-empty-line-before | 在注释前要求或禁止空白行(可自动修复) |
indentation | 指定缩进(可自动修复) |
max-empty-lines | 限制相邻的空行数(可自动修复) |
max-line-length | 限制一行的长度 |
no-eol-whitespace | 禁止行尾空格(可自动修复) |
at-rule-name-newline-after | 在规则名称后需要换行符 |
- stylelint进行忽略检查
忽略所有规则:
/* stylelint-disable */
a {}
/* stylelint-enable */
忽略特定规则:
/* stylelint-disable selector-no-id, declaration-no-important */
#id {
color: pink !important;
}
/* stylelint-enable selector-no-id, declaration-no-important */
忽略某一行:
#id { /* stylelint-disable-line */
color: pink !important; /* stylelint-disable-line declaration-no-important */
}
忽略下一行:
#id {
/* stylelint-disable-next-line declaration-no-important */
color: pink !important;
}
支持叠加使用:
/* stylelint-disable */
/* stylelint-enable foo */
/* stylelint-disable foo */
/* stylelint-enable */
/* stylelint-disable foo, bar */
/* stylelint-disable baz */
/* stylelint-enable baz, bar */
/* stylelint-enable foo */
stylelint-config-recommended文档地址
- stylelint资源集合推荐
更多关于stylelint的推荐,请查阅stylelint资源集合地址
🌟🌟校验css属性摆放顺序
其实,有css开发经验的小伙伴,在书写css顺序的基本上都有那么一套方法论的,比如很多人会常识性的把会影响到元素布局的属性写前面,像position | display
,然后是宽高内外边距等width | height | padding | margin
。其实除了css属性,还有更多的比如变量、预处理器中的混入等等,在css规范中,我们对这些属性摆放顺序也是可以进行校验和自动修复css的。
通过stylelint-order插件,可以设置css属性的摆放顺序等,比如什么属性应该放在前面,什么属性应该放在后面。下面看看如何使用到项目中去吧:
- 安装方法
# 前提是安装了stylelint
cnpm i stylelint-order -D
- 在stylelint的配置文件中进行配置
module.exports = {
extends: [
// css规范配置
'stylelint-config-standard',
],
// 通过插件使用stylelint-order
plugins: ['stylelint-order'],
rules: {
'order/order': [
'custom-properties', // eg: --property: 10px;
'dollar-variables', // eg: $variable
'at-variables', // eg: @someVar
'declarations', // css属性,display: flex
'rules', // 嵌套的属性,eg: .head { .top: {...} },这里.top{}就是嵌套在.head中的集合
// 'at-rules', // 嵌套的属性中含有
// 'less-mixins' // less的mixin,eg: .mixin()
],
'order/properties-order': [
'width',
'height'
// ...
]
}
}
注意注意:所有的rules的插件配置,都是在对象属性中指定修改那个插件的配置,比如修改的order,然后可以是修改order/order
和order/properties-orde
。如上所示,我们可以在order/order
中配置大的规则集合,比如custom-properties
类型的内容放前面,css属性的集合放在后面等,这是大的方向。而order/properties-order
则是具体配置css属性相关的摆放顺序,比如width
放前面,紧接着是height
等。
关于order/properties-order
的设置规则可以查阅文档。github上也有一些配置好的方案(虽然star数不多),比如官方提到的这些资源(我这里截了个图,可以去文档查看):
🌟🌟修复css顺序的集成方案
stylelint-config-recess-order是对stylelint-order的一层封装,使用起来比较简单,把相关的order配置都写好了。
- 安装方法
cnpm i stylelint-config-recess-order -D
- 因为该插件已经内置了stylelint-order,所以不需要重复安装。
// 使用方式
module.exports = {
extends: [
'stylelint-config-standard',
'stylelint-config-recess-order'
]
}
- 其规则列表:
module.exports = {
plugins: ['stylelint-order'],
rules: {
'order/properties-order': [{
// Must be first.
properties: ['all'],
},
{
// Position.
properties: ['position', 'top', 'right', 'bottom', 'left', 'z-index', ],
},
{
// Display mode.
properties: ['box-sizing', 'display', ],
},
{
// Flexible boxes.
properties: ['flex', 'flex-basis', 'flex-direction', 'flex-flow', 'flex-grow', 'flex-shrink', 'flex-wrap', ],
},
{
// Grid layout.
properties: ['grid', 'grid-area', 'grid-template', 'grid-template-areas', 'grid-template-rows', 'grid-template-columns', 'grid-row', 'grid-row-start', 'grid-row-end', 'grid-column', 'grid-column-start', 'grid-column-end', 'grid-auto-rows', 'grid-auto-columns', 'grid-auto-flow', 'grid-gap', 'grid-row-gap', 'grid-column-gap', ],
},
{
// Align.
properties: ['align-content', 'align-items', 'align-self'],
},
{
// Justify.
properties: ['justify-content', 'justify-items', 'justify-self', ],
},
{
// Order.
properties: ['order'],
},
{
// Box model.
properties: ['float', 'width', 'min-width', 'max-width', 'height', 'min-height', 'max-height', 'padding', 'padding-top', 'padding-right', 'padding-bottom', 'padding-left', 'margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left', 'overflow', 'overflow-x', 'overflow-y', '-webkit-overflow-scrolling', '-ms-overflow-x', '-ms-overflow-y', '-ms-overflow-style', 'clip', 'clear', ],
},
{
// Typography.
properties: ['font', 'font-family', 'font-size', 'font-style', 'font-weight', 'font-variant', 'font-size-adjust', 'font-stretch', 'font-effect', 'font-emphasize', 'font-emphasize-position', 'font-emphasize-style', '-webkit-font-smoothing', '-moz-osx-font-smoothing', 'font-smooth', 'hyphens', 'line-height', 'color', 'text-align', 'text-align-last', 'text-emphasis', 'text-emphasis-color', 'text-emphasis-style', 'text-emphasis-position', 'text-decoration', 'text-indent', 'text-justify', 'text-outline', '-ms-text-overflow', 'text-overflow', 'text-overflow-ellipsis', 'text-overflow-mode', 'text-shadow', 'text-transform', 'text-wrap', '-webkit-text-size-adjust', '-ms-text-size-adjust', 'letter-spacing', 'word-break', 'word-spacing', 'word-wrap', // Legacy name for `overflow-wrap`
'overflow-wrap', 'tab-size', 'white-space', 'vertical-align', 'list-style', 'list-style-position', 'list-style-type', 'list-style-image', ],
},
{
// Accessibility & Interactions.
properties: ['pointer-events', '-ms-touch-action', 'touch-action', 'cursor', 'visibility', 'zoom', 'table-layout', 'empty-cells', 'caption-side', 'border-spacing', 'border-collapse', 'content', 'quotes', 'counter-reset', 'counter-increment', 'resize', 'user-select', 'nav-index', 'nav-up', 'nav-right', 'nav-down', 'nav-left', ],
},
{
// Background & Borders.
properties: ['background', 'background-color', 'background-image', "-ms-filter:\\'progid:DXImageTransform.Microsoft.gradient", 'filter:progid:DXImageTransform.Microsoft.gradient', 'filter:progid:DXImageTransform.Microsoft.AlphaImageLoader', 'filter', 'background-repeat', 'background-attachment', 'background-position', 'background-position-x', 'background-position-y', 'background-clip', 'background-origin', 'background-size', 'background-blend-mode', 'isolation', 'border', 'border-color', 'border-style', 'border-width', 'border-top', 'border-top-color', 'border-top-style', 'border-top-width', 'border-right', 'border-right-color', 'border-right-style', 'border-right-width', 'border-bottom', 'border-bottom-color', 'border-bottom-style', 'border-bottom-width', 'border-left', 'border-left-color', 'border-left-style', 'border-left-width', 'border-radius', 'border-top-left-radius', 'border-top-right-radius', 'border-bottom-right-radius', 'border-bottom-left-radius', 'border-image', 'border-image-source', 'border-image-slice', 'border-image-width', 'border-image-outset', 'border-image-repeat', 'outline', 'outline-width', 'outline-style', 'outline-color', 'outline-offset', 'box-shadow', 'mix-blend-mode', 'filter:progid:DXImageTransform.Microsoft.Alpha(Opacity', "-ms-filter:\\'progid:DXImageTransform.Microsoft.Alpha", 'opacity', '-ms-interpolation-mode', ],
},
{
// SVG Presentation Attributes.
properties: ['alignment-baseline', 'baseline-shift', 'dominant-baseline', 'text-anchor', 'word-spacing', 'writing-mode',
'fill', 'fill-opacity', 'fill-rule', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width',
'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'flood-color', 'flood-opacity', 'image-rendering', 'lighting-color', 'marker-start', 'marker-mid', 'marker-end', 'mask', 'shape-rendering', 'stop-color', 'stop-opacity', ],
},
{
// Transitions & Animation.
properties: ['transition', 'transition-delay', 'transition-timing-function', 'transition-duration', 'transition-property', 'transform', 'transform-origin', 'animation', 'animation-name', 'animation-duration', 'animation-play-state', 'animation-timing-function', 'animation-delay', 'animation-iteration-count', 'animation-direction', ],
},
],
},
}
注意一点,如果是未在上面列表进行定义的属性,则放在任何属性的任何位置都可以,但是也可以设置特定位置(比如,未定义的属性只能放在其他属性前面、后面等等)。
stylelint-order
会默认读取stylelint
的fix选项,如果开启了,则自动会进行fix。也可以对stylelint-order
自定义。
- 结合husky使用
// package.json中的husky配置
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
}
// .lintstagedrc配置
{
"./src/*.{css,less,html,htm,vue}": [
"stylelint --fix"
]
}
- vue中配置
下面也顺便也现代的vue项目演示一下怎么使用配置,react也基本类似,都是配置webpack。首先安装webpack对应的插件:
# stylelint和其webpack的插件
cnpm i -D stylelint stylelint-webpack-plugin
# 配置规范和order配置
cnpm i stylelint-config-standard stylelint-config-recess-order -D
使用方式,在webpack.config.js
进行配置:
const StyleLintPlugin = require('stylelint-webpack-plugin');
module.exports = {
// ... 其它选项
plugins: [
new StyleLintPlugin({
// 指定检测的文件
files: ['**/*.{vue,htm,html,css,sss,less,scss,sass}'],
// 启动自动修复
fix: true,
cache: true // 启用缓存
})
]
}
// cli4写法
/**
* 添加stylelint插件
*/
config.plugin('stylelint')
.use(StyleLintPlugin, [{
// 指定检测的文件
files: ['./src/**/*.{vue,css,less}'],
// 启动自动修复
fix: true,
cache: true, // 启用缓存
}])
本地的配置文件:
// .stylelintrc.js文件
module.exports = {
extends: [
'stylelint-config-standard',
'stylelint-config-recess-order'
],
}
参数 | 说明 |
---|---|
files | 指定针对哪些文件进行lint |
fix | 是否自动修复 |
cache | 启动stylelint的缓存功能,极大提升速度 |
🌟🌟校验BEM命名规范
基本上css命名规范,业界比较多的有suit和bem,如果还不清楚这些命名规范的可以先学习一下。
我们可以通过postcss-bem-linter进行css的命名规范检查,其在styelint中支持的插件为stylelint-selector-bem-pattern。下面说明postcss-bem-linter
相关的一些配置等:
- 安装postcss-bem-linter
# postcss-bem-linter的安装
# 如果是作为stylint插件使用,则不需要安装
cnpm i postcss-bem-linter -D
- 安装stylelint-selector-bem-pattern
# 如果是在stylelint中使用bemlint,则只需安装该插件即可
cnpm i stylelint-selector-bem-pattern -D
- stylelint中使用stylelint-selector-bem-pattern
module.exports = {
plugins: [
'stylelint-selector-bem-pattern'
],
rules: {
'plugin/selector-bem-pattern': {
// 选择Preset Patterns,支持suit和bem两种,默认suit规范;
// 不管哪种都需要手动指定,因为该插件未给源插件默认指定
'preset': 'bem',
/**
* 自定义模式规则
* 指定组合的选择器检查规则,其实就是指定class名称规则
* 支持正则字符串、返回正则的函数、包含2个选项参数的对象等写法
*/
componentSelectors: {
// 只初始的选择器规则(可以理解为外层的class规则)
initial: '^\\.{componentName}(?:__[-a-z]+)?(?:--[a-z]+)?$',
// 可以理解为外层以内的选择器的规则,
// 如果不指定,则跟initial同样的规则,
// 注意这里配置的时候比上面少一个问号,
// 是希望内层就不应该只有componentName作为选择器了
combined: '^\\.{componentName}(?:__[-a-z]+)(?:--[a-z]+)?$'
},
"utilitySelectors": "^\\.u-[a-z]+$",
ignoreSelectors: ['^\\.el-', '/deep/', '>>>', '^\\.icon-'],
ignoreCustomProperties: [],
}
}
}
作为插件进行配置即可,通过rules['plugin/selector-bem-pattern]
进行配置参数。stylelint-selector-bem-pattern
只是对postcss-bem-linter
的封装,所以本质是对postcss-bem-linter
进行参数配置,而stylelint-selector-bem-pattern
是没有参数的。但是需要注意的一点是: postcss-bem-linter默认是规则是suit,但是stylelint-selector-bem-pattern却没有给postcss-bem-linter默认规则,所以即使使用suit也需要手动指定,如上preset参数
🌟🌟详解postcss-bem-linter
postcss-bem-linter的文档看起来刚开始还是稍微有些饶人的,对于不熟悉的小伙伴可能会有写绊脚的东西。所以这里,我把一些基本常见的会用到的再介绍一下,帮助小伙伴们快速理解和上手:
- 两种mode:default和weak
默认为default模式,即选择器和自定义属性,都必须符合lint规则检测;weak模式,只需要最外层的符合lint规则即可,嵌套在里面的选择器、自定义属性等不需要符合规则。一般不做设置即可,也可以在文件内通过注释特别指定为weak模式:
/** @define app; weak */
.app--active {
color: #f00;
}
- 定义pattern模式
支持suit
和bem
两种,遵循其对应的命名规范,详情可以查阅其规范文档:suit文档、bem文档
// 在stylelint中使用:
rules: {
'plugin/selector-bem-pattern': {
preset: 'bem'
}
}
- 定义组件名称的lint规则
componentName可以是正则表达式、返回RegExp()的函数:
rules: {
'plugin/selector-bem-pattern': {
preset: 'bem',
componentName: '[a-z]+',
}
}
- 组件内,通过组件注释的方式定义组件名称
定义组件名,比如app:
/** @define app */
定义utilities(工具选择器),这个名称是定死的
组件内(或者其他文件)通过注释定义utilities,则选择选择器必须满足配置参数中的规则
/** @define utilities */
例如配置规则,则所有的工具选择器必须是.u-开头+字母的组合:
rules: {
'plugin/selector-bem-pattern': {
preset: 'bem',
utilitySelectors: '^\\.u-[a-z]+$',
}
}
可组合使用:
/** @define app */
/** @define app2 */
/** @define app3 */
/** @define utilities */
- 指定选择器的组合规则
/**
* 指定组合的选择器检查规则,其实就是指定class名称规则
* 支持正则字符串、返回正则的函数、包含2个选项参数的对象等写法
*/
componentSelectors: {
// 只初始的选择器规则(可以理解为外层的class规则)
initial: '^\\.{componentName}(?:__[-a-z]+)?(?:--[a-z]+)?$',
// 可以理解为外层yi
combined: '^\\.{componentName}(?:__[-a-z]+)(?:--[a-z]+)?$'
},
- 忽略某些选择器检查
/**
* 可以在配置参数中,直接配置需要忽略的选择器,
* 比如,我们组件内覆盖第三方样式的时候,会写第三方样式
* 常见的例如.el-xxxx等,在配置中直接忽略.el-开头的class
*/
rules: {
// 'max-nesting-depth': 4,
'plugin/selector-bem-pattern': {
preset: 'bem',
// 忽略.el开头的选择器验证
ignoreSelectors: ['^\\.el-', '/deep/', '>>>', '^\\.icon-'],
// 忽略自定义属性相关的验证
ignoreCustomProperties: [],
}
}
这里注意下,可以通过ignoreSelectors
忽略我们日常开发中可能常用的deep、.icon
等选择器对lint工具的干扰。
- css中直接忽略下一行的选择权验证
注意,只对下一行的选择器进行的忽略:
/* postcss-bem-linter: ignore */
.xxx-ssssss {
border-right: 0;
}
- 最后放一个基本stylelint的配置
module.exports = {
extends: [
// 标准集合,继承自stylelint-config-recommend
'stylelint-config-standard',
// 内置stylelint-order作为排序
'stylelint-config-recess-order'
],
plugins: [
'stylelint-selector-bem-pattern'
],
rules: {
'plugin/selector-bem-pattern': {
/**
* 选择Preset Patterns,支持suit和bem两种,默认suit规范;
* 不管哪种都需要手动指定,因为该插件未给源插件默认指定
*/
preset: 'bem',
// 知道组件名称的检查规则
componentName: '[a-z]+',
/**
* 指定组合的选择器检查规则,其实就是指定class名称规则
* 支持正则字符串、返回正则的函数、包含2个选项参数的对象等写法
*/
componentSelectors: {
// 只初始的选择器规则(可以理解为外层的class规则)
initial: '^\\.{componentName}(?:__[-a-z]+)?(?:--[a-z]+)?$',
// 可以理解为外层yi
combined: '^\\.{componentName}(?:__[-a-z]+)(?:--[a-z]+)?$'
},
utilitySelectors: '^\\.u-[a-z]+$',
ignoreSelectors: ['^\\.el-', '/deep/', '>>>', '^\\.icon-'],
ignoreCustomProperties: [],
}
}
}
🌟🌟校验HTML代码规范
上面讲完了style部分的内容,下面来看看html如何约束。这里讲解一款用于lint html代码规范的工具htmlhint。
- 安装
cnpm i htmlhint -D
# 如果是在webpack中使用,则只安装其loader
cnpm i htmlhint-loader -D
- 配置
根目录下新建配置文件.htmlhintrc
,注意只能是json语法:
{
"tagname-lowercase": false,
"attr-lowercase": true,
"attr-value-double-quotes": true,
"alt-require": true,
"tag-pair": true,
"src-not-empty": true,
"id-class-ad-disabled": true,
"id-unique": true,
"inline-style-disabled": true
}
- 配置说明
配置比较少,也就30个左右吧,下面说下常见的:
配置 | 说明 | 类型 |
---|---|---|
tagname-lowercase | html标签小写 | boolean |
attr-lowercase | html属性小写 | boolean |
attr-value-double-quotes | 属性值必须要有双引号 | boolean |
alt-require | img等元素必须要有alt属性 | boolean |
tag-pair | 标签必须匹配正确 | boolean |
src-not-empty | src属性不能为空 | boolean |
id-class-ad-disabled | id或class不能使用ad | boolean |
id-unique | id必须唯一 | boolean |
inline-style-disabled | 不允许出现行内style | boolean |
- 配置lint-staged使用
// .lintstagedrc.js
module.exports = {
'./src/**/*.{vue,ts,tsx,js,jsx}': [ 'eslint --fix' ],
'./src/**/*.{vue,css,less}': [ 'stylelint --fix' ],
'./src/**/*.vue': [ 'htmlhint --config ./.htmlhintrc' ],
}
注意,只对vue文件使用了该htmlhint-loader
,目前测试其和webpack-html-loader
冲突,所以舍弃了对index.html模板的检查。
- vue中使用
在vue.config.js文件中进行配置:
config.module
.rule('html')
.enforce('pre')
.test(/\.(vue)(\?.*)?$/)
.exclude
.add(path.join(__dirname, 'node_modules'))
.end()
.use('htmlhint-loader')
.loader('htmlhint-loader')
.tap(() => ({ configFile: './.htmlhintrc' }))
.end()
🌟🌟校验JS规范
ESLint是js的代码规范lint和风格检查工具,这个大家现在cli创建的项目基本是标配了,相信大家都是有些熟悉的。同类型的还有JSLint、JSHint,但是基本已经out了。prettier是代码美化的工具,可以和ESLint配合使用,两者的冲突规则有单独的库可以进行覆盖。更多的,个人觉得ESLint可能基本够用了。
- 安装
cnpm i eslint -D
- 配置文件
根目录创建.eslintrc
或.eslintrc.js
文件
// .eslintrc.js
module.exports = {
rules: {
semi: ["", "always"],
quotes: ["error", "double"]
}
}
支持的所有配置文件类型,如图:
- rules
{
"rules": {
"semi": ["error", "always"],
"quotes": ["error", "double"]
}
}
key是规则名称,数组第一项为规则等级(关闭:0 | off、警告: 1 | warn、错误: 2 | errors)
- 通过注释关闭规则
// 针对某一段代码关闭指定规则
/* eslint-disable no-alert, no-console */
alert('foo');
console.log('bar');
/* eslint-enable no-alert, no-console */
// 关闭整个文件规则
// 在文件顶部加入该注释
/* eslint-disable */
// 针对指定行忽略规则
alert('foo'); // eslint-disable-line
// eslint-disable-next-line
alert('foo');
/* eslint-disable-next-line */
alert('foo');
alert('foo'); /* eslint-disable-line */
- plugin
插件配置可以省略包名的前缀eslint-plugin-
{
"plugins": [
"react",
// 等同于
"eslint-plugin-react"
]
}
- extends
- 覆盖设置
可以对指定的文件重新指定一些配置参数,比如需要忽略的文件等。需要忽略的文件也可以通过.eslintignore
指定。
{
"overrides": [
{
"files": ["bin/*.js", "lib/*.js"],
"excludedFiles": "*.test.js",
"rules": {
"quotes": ["error", "single"]
}
}
]
}
- vue中如何使用eslint
// 配合webpack,在vue.config.js中
module.exports = {
publicPath: process.env.PUBLIC_PATH,
chainWebpack: config => {
config.module
.rule('eslint')
.use('eslint-loader')
.loader('eslint-loader')
.tap(options => {
// 让eslint在save时自动fix code
options.fix = true
// 开启缓存
options.cache = true
return options
}).end()
}
}
更多webpack中的eslint配置,参考eslint-loader文档
🌟🌟详解eslint-plugin-vue
vue-cli4初始化的js项目eslint部分配置:
vue-cli4初始化的ts项目eslint部分配置:
ts版本和js版本的项目,区别是增加了一个typescript/recommend规则集合
plugin:vue/essential
是eslint-plugin-vue中针对vue2+的基本错误lint(即优先级为A的级别),可以如果需要开启其他规则集合的,可以继续增加配置:
{
extends: [
'plugin:vue/essential',
'@vue/airbnb',
'@vue/typescript/recommended',
// 开启优先级为B的校验
'plugin:vue/strongly-recommended',
// 开启优先级为C的校验
'plugin:vue/strongly-recommended'
],
rules: {
// 注意,这里个人不喜欢html标签的>在多行时自动换行
// 所以关闭该特性
"vue/html-closing-bracket-newline": ["error", {
"singleline": "never",
"multiline": "never"
}]
},
}
注意eslint-plugin-vue的版本,目前cli给安装的是6.2.2稳定版本,新的版本以及到7.0.0-beta.1(当前时间2020-08-12),所以文档上的一些新的lint,是基于新版本的(例如自定义事件名称必须使用连字符而不是小驼峰等),而文档也是新版本的文档,这个稍微注意一下。
🌟🌟JSDoc代码注释规范
上面说到了git、html、css、js以及vue的规范,那么除此之外,还有注释的规范。良好的注释可以减少代码的理解成本和辅助自动生成文档等功能。👇下面我们将介绍介绍JSDoc的注释规范:
JSDoc定义了一系列块级标记,用于说明各种注释内容,基本的写法为:
/**
* 你的注释内容
*
* @param { number } id - 用户id标识
* @returns { object } 返回查到的用户数据
*/
下面看看常见的标记有哪些,以及有哪些作用:
- @abstract 抽象类、抽象方法
- @class 描述一个类或构造函数,指明需要new来调用,别名@constructor
- @extends 描述继承自哪个父类
- @override 描述覆盖了父类的方法
- @callback 描述一个回调函数
- @description 定义描述,如果放在首行,可以省略该标记
/**
* 人
* @class
* @extends Animal
*/
class Person extends Animal {
private name: string;
constructor(name: string) {
super(name);
this.name = name;
}
/**
* 说出自己的姓名
* @param { Person~sayCallback } cb - say方法的回调函数
*/
say(cb) {
console.log(this.name);
typeof cb === 'function' && cb(this);
}
}
/**
* 这个回调显示为Person类的一部分
* @callback Person~sayCallback
* @param {this} cb Person实例
*/
- @constant 描述一个常量
- @type 描述一个类型,可以与@param、@constant等结合使用
- @default 定义默认值,如果是简单值,可以让jsdoc自己获取,直接写@default即可,也可以自己定义默认值
/**
* @constant
* @type { string }
* @default
*/
const COLORT = '#f00'
- @enum 枚举
- @readonly
/**
* 枚举move状态
* @enum {number}
* @readonly
*/
enum MoveStatus {
up = 1,
right,
bottom,
left
}
- @example 描述一个使用示例
/**
* 格式化数字单位,如果是数字则自动转变为数字加px结尾
*
* @example
* formatUiSize(20) // 返回 '20px'
* @example
* formatUiSize('20px') // 返回 '20px'
* @param {number | string} val - 待格式化的单位
* @returns {string} 返回格式化后的单位
*/
export const formatUiSize = (val: string | number): string | number => (typeof val === 'number'
? `${val}px`
: val);
- @function、@method表示一个函数或方法
- @global 描述一个全局变量
- @mixin 描述一个混入对象
- @mixes 混入了其他对象
- @param 对某个函数的参数的各项说明,包括参数名、参数数据类型、描述等
/**
* @param {string} somebody - Somebody's name.
*/
function sayHello(somebody) {
alert('Hello ' + somebody);
}
// 如果参数是一个对象,且有很多属性参数
/**
* Assign the project to an employee.
* @param {Object} employee - The employee who is responsible for the project.
* @param {string} employee.name - The name of the employee.
* @param {string} employee.department - The employee's department.
*/
Project.prototype.assign = function(employee) {
// ...
};
/**
* Assign the project to a list of employees.
* @param {Object[]} employees - The employees who are responsible for the project.
* @param {string} employees[].name - The name of an employee.
* @param {string} employees[].department - The employee's department.
*/
Project.prototype.assign = function(employees) {
// ...
};
// 可选参数和默认值
/**
* @param {string} [somebody=John Doe] - Somebody's name.
*/
function sayHello(somebody) {
if (!somebody) {
somebody = 'John Doe';
}
alert('Hello ' + somebody);
}
- @returns 描述一个返回值,用法和@param类似
- @throws 描述函数可能抛出一个错误
/**
* @throws Will throw an error if the argument is null.
*/
function bar(x) {}
🌟🌟校验注释是否规范
通过eslint-plugin-jsdoc
插件检测jsdoc类型的注释是否符合规范。
- 基本使用
module.exports = {
plugins: [
'jsdoc'
],
extends: [
// 'plugin:vue/essential',
// '@vue/airbnb',
// '@vue/typescript/recommended',
// 开启所有recommended的规则
'plugin:jsdoc/recommended',
],
rules: {
// 检测无效的填充块
'jsdoc/check-indentation': 'warn',
}
}
- 所有检测规则
"rules": {
// *号必须按标准对齐
"jsdoc/check-alignment": 1, // Recommended
// 检测example中的js代码是否符合eslint规则
"jsdoc/check-examples": 1,
// 检测无效的填充块
"jsdoc/check-indentation": 1
// 检测param的名称是否与函数中的参数名称一致
"jsdoc/check-param-names": 1, // Recommended
"jsdoc/check-syntax": 1,
// 检测块标记名称是否正确,例如@paramsss是错误的
"jsdoc/check-tag-names": 1, // Recommended
// 检测参数类型是否正确,例如{object},可自动修复大小写
"jsdoc/check-types": 1, // Recommended
"jsdoc/implements-on-classes": 1, // Recommended
// 通过正则表达式限制描述的内容
// 比如限制只能使用英文,首字母大写等场景
"jsdoc/match-description": 1,
// 强制描述的内容后面必须有一个空行,也可以配置没有
"jsdoc/newline-after-description": 1, // Recommended
"jsdoc/no-types": 1,
// 不允许出现未定义的参数类型,例如{strrrrr}
"jsdoc/no-undefined-types": 1, // Recommended
// 要求所有函数都要有描述
"jsdoc/require-description": 1,
// 要求完整的标记,例如描述必须使用@description,而不是直接文字描述
"jsdoc/require-description-complete-sentence": 1,
// 要求所有函数必须要有example
"jsdoc/require-example": 1,
"jsdoc/require-hyphen-before-param-description": 1,
// 检查类声明和函数上是否有jsdoc注释
"jsdoc/require-jsdoc": 1, // Recommended
// 要求函数所有参数写param
"jsdoc/require-param": 1, // Recommended
"jsdoc/require-param-description": 1, // Recommended
// 要求每个@param标签都要有description
"jsdoc/require-param-name": 1, // Recommended
// 要求每一个@param标签都要有type类型参数
"jsdoc/require-param-type": 1, // Recommended
"jsdoc/require-returns": 1, // Recommended
// 当使用@returns后,检测函数中是否有符合条件的返回值
"jsdoc/require-returns-check": 1, // Recommended
// @returns必须要有description
"jsdoc/require-returns-description": 1, // Recommended
// @returns必须要有返回类型参数
"jsdoc/require-returns-type": 1, // Recommended
// 验证类型的有效性
"jsdoc/valid-types": 1 // Recommended
}
注意,eslintrc.js配置文件中的'plugin:jsdoc/recommended'
会自动开启上述lint规则中的所有Recommended规则。
最后
百尺竿头、日进一步,我是你们的老朋友愣锤!!!更多内容推荐,记得关注、点赞不迷路哦~~~