每个开发者都有自己的习惯,代码的风格也不尽相同,比如缩进的格数,结尾加不加分号,定义函数有没有空格,这些看似好像不起眼的风格,但在多人开发的时候,代码commit提交,可能就会出现一大片冲突,或者过多修改过的情况,对项目管理和维护是极其不友好的,而且在review,阅读项目代码的时候,一个项目中出现几种,甚至10几种的代码风格,我相信没人受得了这事,人都有一种惯性思维,习惯看一种风格,切换到其他风格可能需要适应一下,更别说人手一套风格的项目了,这肯定是不能接受的
代码规范一致性很重要,经历过多人协作项目开发的都知道,代码风格不一样,在commit提交代码的时候,代码可能是一样的,但每行多一个空格,少一个空格,都可能会出现大面积的差异,和冲突,diff比较冲突解决起来就这个环节就能自闭了,这个问题很严重,必须得解决
幸运的是,我们可以通过工具来解决这个看似很棘手的问题:
ESLint + Prettier + husky + lint-staged
这套代码自动化规范工具能很好的解决前端代码规范问题
先来说下每个工具都是干嘛的,最后如何组合在一起在项目中去使用
ESLint: 可参考
ESLint是一个JavaScript代码静态检查工具,可以检查JavaScript的语法错误
,提示潜在的bug,可以有效提高代码质量,维持前端团队高度一致的编码风格。ESLint不但提供一些默认的规则,也提供用户自定义规则来约束所写的JavaScript代码。
所有都是可拔插的:
- 内置规则和自定义规则共用一套规则 API
- 内置的格式化方法和自定义的格式化方法共用一套格式化 API
- 额外的规则和格式化方法能够在运行时指定
- 规则和对应的格式化方法并不强制捆绑使用
每条规则:
- 各自独立
- 可以开启或关闭(没有什么可以被认为“太重要所以不能关闭”)
- 可以将结果设置成警告或者错误
如下等等,很多对应使用场景都有对应的规则。
配置名 | 作用 |
---|---|
array-bracket-newline | 在数组开括号后和闭括号前强制换行 |
array-bracket-spacing | 强制数组方括号中使用一致的空格 |
array-element-newline | 强制数组元素间出现换行 |
block-spacing | 禁止或强制在代码块中开括号前和闭括号后有空格 |
brace-style | 强制在代码块中使用一致的大括号风格 |
更多规则,可参考官方文档
这些规则都是通过一个.eslintrc.js
文件和.eslintignore
文件去管理的
.eslintrc.js
配置的是代码检查时的代码风格
.eslintignore
配置的是对于哪些文件不做检查,和.gitignore
的作用一样
.eslintignore
示例配置如下:
node_modules/
dist/
.yarn/
src/assets/
.eslintrc.js
示例配置如下
module.exports = {
"extends": ["airbnb-base"],
"env": {
"es6": true,
"node": true
},
"rules": {
"comma-dangle": ["error", "never"], // 要求或禁止末尾逗号:不允许逗号
"indent": ["error", 2], // JavaScript代码强制使用一致的缩进:2格缩进
"semi": ["error", "never"], // 不使用分号
"arrow-parens": ["error", "as-needed"], // 箭头函数的参数可以不使用圆括号
"linebreak-style": "off", // 取消换行符\n或\r\n的验证
"object-curly-newline": ["error", {
"consistent": true }], // 花括号内的换行符不一定要格式一致
"function-paren-newline": "off", // 不验证函数括号内的换行
"import/extensions": "off", // 取消对文件扩展名的验证
"no-param-reassign": "off", // 允许对函数参数进行再赋值
"no-underscore-dangle": "off", // 允许在标识符中使用下划线
"no-use-before-define": "off", // 允许变量和函数在定义前使用
"no-unused-expressions": "off", // 允许使用未使用过的表达式,以此来支持a && a()的代码形式
"no-console": "off", // 启用console控制台
"consistent-return": "off", // 关闭函数中return的检测
"no-shadow": "off", // 可以使用同名变量,
"newline-per-chained-call": "off", //取消方法链调用中的换行符的检测
"import/newline-after-import": "off"
}
}
Prettier
Prettier是很火的一个代码美化工具,其中文意思是“漂亮的、机灵的”,它能够解析代码,使用你自己设定的规则来对代码的风格进行自动格式化处理,例如 缩进使用4个空格。
他的整个圈子很强大,有基于各种编辑器的插件(vs code,atom),有脚本类的,有插件类的(eslint的插件eslint-plugin-prettier)
。
Prettier 是一个前端的代码格式化工具,支持列表如下:
- JavaScript,包括 ES2017
- JSX
- Flow
- TypeScript
- CSS, LESS, and SCSS
- JSON
- GraphQL
简而言之,这个工具能够使输出代码保持风格一致。
和eslint的关系: eslint能检查出代码的错误,风格不合规则的问题,然后给我们报错,告诉我们在哪里错了,需要我们手动一行一行去改,而Prettier能帮我们去格式化不规范代码,变成规范的,不再需要自己手动去改了,所以在实际项目中eslint和Prettier往往是配合使用的,eslint检查出错误,Prettier格式化不规范代码,这也就需要eslint的检查规则和Prettier的格式化规则需要保持一致
配合使用方式:把Prettier当作eslint的插件去使用
安装:
npm i -D prettier eslint-plugin-prettier
在eslint的配置文件中添加配置
在.eslintrc
或者 .eslintrc.json
或者 .eslintrc.js
文件中
{
"plugins": ["prettier"],
"rules": {
"prettier/prettier": "error"
}
}
Husky:可参考
如同其他许多的版本控制系统一样,Git 也具有在特定事件发生之前或之后执行特定脚本代码功能(从概念上类比,就与监听事件、触发器之类的东西类似)。Git Hooks 就是那些在Git执行特定事件(如commit、push、receive等)后触发运行的脚本。
简单的说就是husky这个工具能在一些git的命令上埋下钩子,当我们使用这些命令的时候就会触发之前埋下的脚本,比如常用的pre-commit
和commit-msg
钩子,能在我们git commit
之前调用一下,和提交commit message
时再调用一下
场景: 可用于commit提交代码时,检查代码规范和格式化,也可用于检查commit message是否符合message规范
lint-staged
解决的痛点: 每次修改一个文件就给所有文件执行一次lint检查
在代码提交之前,进行代码规则检查能够确保进入git库的代码都是符合代码规则的。但是整个项目上运行lint速度会很慢,lint-staged能够让lint只检测暂存区的文件,所以速度很快。
npm i husky lint-staged -D
在这里插入代码片{
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.js": "eslint --fix"
}
}
原理: git commit时触发pre-commit钩子,运行lint-staged命令,对*.js执行eslint命令。eslint要提前配置好。
在项目中的实际组合应用:
目标: 保证项目中的每个开发者,推送到远程仓库时,代码的风格是一致的
思路:
通过lint
工具检查代码规范,通过Prettier
工具格式化不规范的代码
我们可以在 package.json
中设置配置lint
命令,来帮助开发者开发完后通过npm run lint
命令检查代码规范,同时配置lint:fix
命令,在检查完代码后,不规范的代码通过Prettier
格式化
但开发者可能没有代码写完后npm run lint
或者 npm run lint:fix
的习惯怎么办,那就需要通过husky
埋下pre-commit
脚本,commit
之前自动调用npm run lint:fix
检查并且格式化代码
配置过程:
.eslintrc.js
的配置如下:
module.exports = {
env: {
browser: true,
webextensions: true,
jquery: true
},
plugins: ['prettier'],
extends: [
'standard', //使用standard做代码规范
'plugin:prettier/recommended',
'prettier/standard',
],
rules: {
'prettier/prettier': [
'error',
{
endOfLine: 'auto'
}
],
// 继承于standard规则,还包含了import,node,promise,具体规则请看 https://standardjs.com/rules-zhcn.html#javascript-standard-style
'no-useless-escape': 'off', // 禁用不必要的转义,但由于`\$\{${key}\}` 这样的他识别错误,所以关掉它
'prefer-promise-reject-errors': 'off', // 可以使用非error对象作为promise reject的原因 有时直接 reject 一个字符串还是比较方便的
'no-mixed-operators': 'warn', // 混合使用不同的操作符最好用括号括起来明确意图 例如 a && b || c 这样写其实虽然挺直接的,但最好写成 (a && b) || c ,因为standard里是错误,此处改为警告就行
'no-prototype-builtins': 'warn', // 禁止直接使用 Object.prototypes 的内置属性,建议使用 Object.prototype.hasOwnProperty.call(foo, "bar") 这种方式
'max-depth': ['error', 5], // 函数嵌套过深,提醒重构
'max-params': ['error', 7], // 函数参数过多, 注意可以使用对象传参,这里应该调整为5好一点,不过考虑项目有历史代码,先调整为7
'max-nested-callbacks': ['error', 3], // 最大回调深度 为3层
'prefer-rest-params': 'warn', // 建议使用解构 ...args 来代替 arguments , arguments 没有 Array.prototype 方法,所以有点不方便
'no-var': 'error', // 禁止使用 var,必须用 let 或 const
'for-direction': 'error', // 如果一个 for 循环的停止条件永远无法到达,比如,计数器在错误的方向上移动,将陷入无限循环。
'getter-return': 'error', // 每个 getter 都期望有返回值
complexity: ['warn', 5], // 所有代码的独立现行路径条数 例如多个 if else
'no-empty': 'error', // 禁止空块语句
'no-inner-declarations': 'error', // 禁止在嵌套的语句块中出现function 声明
'default-case': 'error', // 要求 Switch 语句中有 Default 分支,如果的确没有 可在最后注释 // No Default
'guard-for-in': 'warn', // 在使用 for in 遍历对象时,会把从原型链继承来的属性也包括进来,最好增加if判断,比如foo.hasOwnProperty(key),不过注意不要违反 no-prototype-builtins
'max-classes-per-file': ['warn', 1], // 每个文件中建议只包含1个类 单一责任原则
'no-alert': 'error', // alert、confirm 和 prompt 被广泛认为是突兀的 UI 元素,应该被一个更合适的自定义的 UI 界面代替。
'no-empty-function': 'error', // 禁止出现空函数,尤其是箭头函数的空语句块,可以在空函数里写一条注释
'node/handle-callback-err': 'warn' // 当你使用回调模式时,遇到error最好处理
}
}
prettier.config.js
配置如下: 格式化的风格,和eslint的风格保持一致
module.exports = {
printWidth: 120, // 每行代码最大长度
tabWidth: 2, // 一个tab代表几个空格数,默认为80
useTabs: false, // 是否使用tab进行缩进,默认为false,表示用空格进行缩减
semi: false, // 声明后带分号
singleQuote: true, // 使用单引号
jsxSingleQuote: true, // 使用单引号
jsxBracketSameLine: true, // 启用jsx语法,> 放在末尾
trailingComma: 'none',
bracketSpacing: true // 对象,数组加空格
}
package.json
文件如下:
{
"name": "xxxx",
"version": "1.0.0",
"description": "xxxx",
"private": false,
"main": "src/index.js",
"repository": "xxxxx",
"author": "xxxx",
"license": "None",
"engines": {
"node": ">=10",
"yarn": ">= 1.0.0"
},
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint . --fix"
},
"dependencies": {
},
"devDependencies": {
"autoprefixer": "^9.7.4",
"husky": "^4.3.0",
"lint-staged": "^10.5.0",
"prettier": "^2.1.2",
"eslint": "^7.11.0",
//通过使用eslint-config-prettier配置:
//能够关闭一些不必要的或者是与prettier冲突的lint选项。这样我们就不会看到一些error同时出现两次
"eslint-config-prettier": "^6.15.0",
"eslint-config-standard": "^15.0.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^3.1.4",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.2",
"eslint-plugin-vue": "^7.1.0",
},
"peerDependencies": {
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{ts,vue,js}": "eslint --fix"
}
}
配置完后,当我们执行npm run lint
或者 npm run lint:fix
后,工具会自动检查或修复代码
或者我们直接git add .
,git commit
提交时,也会触发husky
埋下的钩子,调用lint
去检查代码
因为我在代码中加了个debugger
,eslint --fix
是做不到自动修复,他只会针对格式修复,对于console.log
,和一些debugger
测试的代码,还是需要自己手动删除的
这样我们就能保证推送到远程仓库中的代码风格都是一致的
值得一提的就是,代码规范和commit规范,在项目中往往都是同时使用的,我个人认为他们都是项目管理和项目维护的第一道防线,极其重要