引言
发现每次
commit
的时候总是提交完了才发现少了一个分号,或者多了一个console.log
,想起以前看过的项目里使用了 husky 这个库,可以在 commit 之前做代码校验,如果代码有格式问题,就会禁止提交。
作用
在我们提交代码时,先自动帮我们格式化代码,然后使用eslint检查代码,并自动修复错误,在修复不了的时候,报错给我们。并且报错后此次的commit不会提交。
关于 commitlint, husky, eslint 的具体信息可以见官网。
工具
prettier
。 一个很流行的代码格式化工具,你很容易在编辑器找到实现它的各种插件,像vscode,atom,webstom都可以找到。这里用它在代码提交前做代码格式化。eslint
。 代码检查工具。eslint也可以负责一部分代码格式检查的工作,但是prettier已经做的很好了,所以我便没用eslint的代码格式检查,只让其负责代码错误检查。lint-staged
。在你提交的文件中,执行自定义的指令。don’t let 💩 slip into your codebase. — lint-staged
安装
安装eslint
npm i -D eslint babel-eslint eslint-config-airbnb eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react
复制代码
安装prettier
npm install --save-dev prettier eslint-plugin-prettier eslint-config-prettier复制代码
安装 husky 和 lint-stage
yarn add husky@next # 安装最新版,就不用配置 scripts 脚本了
yarn add lint-stage复制代码
配置
配置 commitlint
commitlint
搭配 husky
的 commit message 钩子后,每次提交 git 版本信息的时候,会根据配置的规则进行校验,若不符合规则会 commit 失败,并提示相应信息。
安装 commitlint 依赖
yarn add @commitlint/{cli,config-conventional}复制代码
新建 commitlint.config.js 文件
module.exports = {
extends: ['@commitlint/config-conventional']
};复制代码
commitlint.config.js 配置文件可以添加自己的规则,这里 @commitlint/config-conventional 提供了官方的规则扩展:
build:主要目的是修改项目构建系统(例如 glup,webpack,rollup 的配置等)的提交
ci:主要目的是修改项目继续集成流程(例如 Travis,Jenkins,GitLab CI,Circle等)的提交
docs:文档更新
feat:新增功能
merge:分支合并 Merge branch ? of ?
fix:bug 修复
perf:性能, 体验优化
refactor:重构代码(既没有新增功能,也没有修复 bug)
style:不影响程序逻辑的代码修改(修改空白字符,格式缩进,补全缺失的分号等,没有改变代码逻辑)
test:新增测试用例或是更新现有测试
revert:回滚某个更早之前的提交
chore:不属于以上类型的其他类型复制代码
配置 package.json 文件
添加 husky 字段
"husky": {
"hooks": {
"commit-msg": "commitlint -e $HUSKY_GIT_PARAMS"
}
},复制代码
测试
git add .
git commit -m "foo: this will fail"复制代码
prettier配置
prettier
代码格式化核心eslint-plugin-prettier
插件,可以让eslint使用prettier规则进行检查,并使用--fix选项。像之前的格式不对时,eslint提示的红线。eslint-config-prettier
插件,之前说了eslint
也会检查代码的格式,这个插件就是关闭所有不必要或可能跟prettier产生冲突的规则。
在eslintrc.json
添加如下配置:
{
"extends": ["airbnb", "plugin:prettier/recommended"],
}复制代码
这个配置做如下三件事:
- 使
eslint-plugin-prettier
生效 - 不符合
prettier/prettier
的规则,会报错。就是之前截图中的红线。 - 使
eslint-config-prettier
生效。就是会覆盖eslint
中与prettier
冲突的配置。
prettier配置文件
prittier
配置文件支持很多种,具体可以看这里。我使用的是.prettierrrc格式,因为试过其他格式,但是只有.prettierrrc,vscode才可以识别。 生成配置可以直接用官网上的try it out,左下角有导出配置。下面这份配置基本上是风格要求的全部了,具体可按照个人爱好进行配置。
{
"printWidth": 120, // 一行最大多少字符
"tabWidth": 2, // tab占用的字符数
"useTabs": false, // 是否使用tab代替空格
"semi": true, // 是否每句后都加分号
"singleQuote": true, // 是否使用单引号
"jsxSingleQuote": false, // jsx是否使用单引号
"trailingComma": "all", // 数组尾逗号。
"bracketSpacing": false, // {foo: xx}还是{ foo: xx }
"jsxBracketSameLine": false, //看官网
"arrowParens": "always" //剪头函数参数是否使用()
}复制代码
配置eslint钩子
.eslintrc.js
*vue开启eslint基本上不用配置,react可以配置自己的
module.exports = {
parser: 'babel-eslint',
plugins: ['react'],
root: true,
env: {
browser: true,
node: true,
es6: true
},
parserOptions: {
ecmaVersion: 6,
sourceType: 'module'
},
globals: {
document: true,
localStorage: true,
window: true,
process: true,
console: true,
navigator: true,
fetch: true,
URL: true
},
rules: {
'no-console': 'off',
'no-alert': 0, //禁止使用alert confirm prompt
'no-var': 0, //禁用var,用let和const代替
'no-catch-shadow': 2, //禁止catch子句参数与外部作用域变量同名
'default-case': 2, //switch语句最后必须有default
'dot-notation': [0, { allowKeywords: true }], //避免不必要的方括号
'no-constant-condition': 2, //禁止在条件中使用常量表达式 if(true) if(1)
'no-dupe-args': 2, //函数参数不能重复
'no-inline-comments': 0, //禁止行内备注
'no-unreachable': 2, //不能有无法执行的代码
'no-unused-vars': [2, { vars: 'all', args: 'after-used' }], //不能有声明后未被使用的变量或参数
'no-unused-expressions': 2, //禁止无用的表达式 | 短路求值和三目运算都允许 0 | 2 都不允许
// 'no-unused-expressions': [
// 2,
// { allowShortCircuit: false, allowTernary: true },
// ], // 允许三目,不允许短路:
'no-mixed-spaces-and-tabs': [2, false], //禁止混用tab和空格
'linebreak-style': [0, 'windows'], //换行风格
'no-multiple-empty-lines': [1, { max: 2 }], //空行最多不能超过2行
'no-extra-semi': 2, //禁止多余的冒号
'no-debugger': 2, //禁止使用debugger
// 'space-before-function-paren': [2, { anonymous: 'never', named: 'never' }], // 函数名后面加空格
// 'space-before-function-paren': ['error', 'always'],
// or
'space-before-function-paren': [
'error',
{
anonymous: 'always',
named: 'always',
asyncArrow: 'always'
}
],
'eol-last': 0, //文件以单一的换行符结束
// eqeqeq: true, //必须使用全等 如果是true,则要求在所有的比较时使用===和!==
// eqnull: true, // 如果是true,则允许使用== null
'lines-around-comment': 0, //行前/行后备注
'operator-linebreak': [2, 'after'], //换行时运算符在行尾还是行首
'prefer-const': 0, //首选const
quotes: [1, 'single'], //引号类型 `` "" ''
'id-match': 0, //命名检测
'array-bracket-spacing': [2, 'always'], // 指定数组的元素之间要以空格隔开(,后面), never参数:[ 之前和 ] 之后不能带空格,always参数:[ 之前和 ] 之后必须带空格
quotes: [2, 'single'], // 全部单引号
// 数组和对象键值对最后一个逗号, never参数:不能带末尾的逗号, always参数:必须带末尾的逗号,
// always-multiline:多行模式必须带逗号,单行模式不能带逗号
'comma-dangle': [2, 'never'],
'computed-property-spacing': [2, 'never'], // 以方括号取对象属性时,[ 后面和 ] 前面是否需要空格, 可选参数 never, always
semi: [2, 'never'], //语句强制分号结尾 不要分号
'eol-last': 2, // 文件末尾强制换行
'semi-spacing': [0, { before: false, after: true }], //分号前后空格
'arrow-body-style': 0, // 不禁止箭头函数直接return对象
strict: 2, //使用严格模式
'use-isnan': 2, //禁止比较时使用NaN,只能用isNaN()
'valid-typeof': 2, //必须使用合法的typeof的值
'space-in-parens': [0, 'always'],
'template-curly-spacing': [2, 'always'],
'array-bracket-spacing': [2, 'always'],
'object-curly-spacing': [2, 'always'],
'computed-property-spacing': [2, 'always'],
'no-multiple-empty-lines': [2, { max: 1, maxEOF: 0, maxBOF: 0 }],
quotes: [1, 'single', 'avoid-escape'],
'no-use-before-define': [2, { functions: false }],
semi: [0, 'never'],
'prefer-const': 1,
'react/prefer-es6-class': 0,
'react/jsx-filename-extension': 0,
'react/jsx-curly-spacing': [2, 'always'],
'react/jsx-indent': [2, 2],
'react/prop-types': [1],
'react/no-array-index-key': [1],
'class-methods-use-this': 'off',
'no-undef': [1],
'no-case-declarations': [1],
'no-return-assign': [1],
'no-param-reassign': [1],
'no-shadow': [1],
camelcase: [1],
'no-unused-vars': 'off',
'no-underscore-dangle': [0, 'always']
}
}
复制代码
husky钩子pre-commit配置
react配置package.json
"husky": {
"hooks": {
"commit-msg": "commitlint -e $HUSKY_GIT_PARAMS",
"pre-commit": "lint-staged", // pre-commit,提交前的钩子
"pre-add": "lint-staged",
"pre-push": "lint-staged"
}
},
"lint-staged": {
// 此处可以配置文件夹和文件类型的范围
"src/**/*.{jsx,txs,ts,js,json,css,md}": [
"prettier --write", // 先使用prettier进行格式化
"eslint --fix", // 再使用eslint进行自动修复
"git add" // 所有通过的话执行git
]
}复制代码
vue配置package.json
"gitHooks": {
"commit-msg": "commitlint -e $HUSKY_GIT_PARAMS",
"pre-commit": "lint-staged"
},
"lint-staged": {
"*.js": [
"vue-cli-service lint",
"git add"
],
"*.vue": [
"vue-cli-service lint",
"git add"
]
}复制代码
husky
会在你提交前,调用pre-commit
钩子,执行lint-staged
,如果代码不符合prettier
配置的规则,会进行格式化;然后再用eslint
的规则进行检查,如果有不符合规则且无法自动修复的,就会停止此次提交。如果都通过了就会讲代码添加到stage,然后commit
。
*如果想跳过校验
使用 --no-verify
指令可以跳过检验规则
git add . && git commit --no-verify -m "代码规范强制提交测试"复制代码
补充说明
关于前端工程打包对每一个开发人员都不可避免,发布线上频繁的build,是很浪费时间的,那么如何避免重复的操作?
其实gitHooks
也可以帮我们做到比如我们做如下配置:
"gitHooks": {
......,
"pre-push": "yarn run build && git add . && git commit -am 'prod build'"
},
"lint-staged": {......}复制代码
这样就完美解决了我们的顾虑,当然此方法也存在一定的缺陷。例如:
- 多人开发需要处理每次build带来的冲突;
- 每次push都会伴随一次build产生;
- 发布上线发现静态资源不是最新的(解决这一类问题有很多办法,比如出一个公共接口每次发布上线手动刷新我们的静态资源等等。)
针对build
工程发布上线本节我们简单介绍一下,后续会为大家详细普及,敬请关注哦!
总结
有人说前端攻城狮是世界上最奇怪的动物,提交代码时用 prettier 把代码排版的很美观,但部署上线时又使用 uglify 把代码压缩的连亲妈都不认了,事实是,如果我们写出来的代码本来就很丑陋,就根本不需要用 uglify。希望读到这里的你能把 Lint 工作流打磨到极致,把更多时间专注在解决真正的问题上,成为真正高效的工程师