背景
在日常需求迭代中,代码的规范与质量是编码的重要一环。Eslint
作为规则扫描器,能够对前端代码进行有效管控,避免出现低级错误,对于前端项目或多或少肯定都会看到 eslint
的相关配置。
但目前存在一些老项目, eslint
的配置仅仅停留在了多年前加的一些 eslint
规则上,没有任何其他动作,导致平常开发中有如下痛点:
- 本地不方便开启保存自动格式化,因为对于老页面,如果开启的话会造成大量的无关
diff
。 - 提交代码的时候会受到莫名其妙的卡控。
- 过
pr
的时候分号、空格、换行各个地方不对齐,逼死强迫症系列。
基于此,前段时间对老项目的 eslint
进行了一次完善,分享一下整个配置和思考的过程。
选取规则
eslint
eslint
规则可以单独一条条配置,也有一些规则的集合比如官方推荐的 eslint:recommended
,框架相关的 plugin:vue/recommended,还有公司开源出来的整套规则比如 Airbnb
的 eslint-config-airbnb,腾讯的 eslint-config-alloy。
选取什么规则不是非常重要,大部分规则集也是类似的,此外本地也可以定义相同的规则名对规则集进行覆盖。
以 alloy
的规则为例,按照 eslint-config-alloy 中的文档安装完相应的 node
包以后,在本地根目录中新建 .eslintrc.js
文件引入相应的规则。
1 | module.exports = { |
如上所示,我们可以在 rules
中定义或者覆盖一些规则。
Prettier
Prettier
是一个代码格式化工具,相比于 eslint
中的代码格式规则,它提供了更少的选项,却更加专业。
相比于 eslint
, Prettier
主要格式样式相关的,比如有没有分号、空格数、一行最大字符数等等,而 eslint
通过解析出代码的 AST
,可以自动格式化或者检测出一些潜在的问题,比如是否允许使用 console
、变量声明但未使用、switch
缺少 defaut
等。
当然 eslint
也可以配置样式相关的规则,但存在一些情况 eslint
无法胜任,因此格式化相关的我们都交给更专业的 Prettier
,安装 Prettier
的 node
包,并且根目录增加配置文件 .prettierrc.js
。
1 | // .prettierrc.js |
编辑器自动修复
这一步我认为是推动 eslint
最重要的一步,大家抗拒项目添加 eslint
一个很大的原因就是本地没有开启实时检查和自动修复,当提交 commit
的时候遇到 eslint
规则卡控就很难受了。
团队内都使用的 VSCode
进行开发,可以安装 Eslint
和 Prettier
插件。
在本地新增 .vscode/settings.json
文件进行插件的配置,并且该文件不忽略 git
,所有人共享。
1 | { |
这个文件是 VSCode
针对当前工程的配置,配置后保存文件的时候插件会自动帮助我们格式化,同时有实时的错误提示。
这里需要注意的一点是,保存的时候会同时进行 prettier
和 eslint
的修复,如果 eslint
也配置了样式相关的规则,此时可能发生冲突,导致自动格式化后会有 eslint
的报错,此时可以将相应的 eslint
规则手动关闭,也可以引入 eslint-config-prettier 这个规则集批量关闭。
commit 卡控
为了保证 eslint
规则的有效,需要在提交 commit
的时候进行检查,如果存在没有修复的 eslint
问题直接终止提交。
直接使用 "husky": "^1.3.1"
和 "lint-staged": "^8.1.5"
两个 node
包,需要注意下版本号,最新的配置有些不同了,下边是该版本下的配置。
1 | "husky": { |
husky
提供了 pre-commit
的钩子,然后 lint-staged
对暂存区代码自动进行格式化,如果出错的话会直接退出。
这样当我们提交 commit
的时候就会运行 eslint
和 prettier
进行代码的格式化。
流水线卡控
虽然上一步对 commit
进行了卡控,但如果 git commit
的时候添加了 -n
参数,卡控检查也就直接跳过了。
如果想彻底的卡控,我们可以在打包流水线上增加一个 lint
的插件进行检查。
这里实现卡控有两种思路:
发布分支和
master
做diff
,仅仅对diff
出的commit
进行eslint
的检查。但这里可能存在两个问题需要注意:
如果本地合并
master
的时候产生了冲突,然后解决冲突会新提交一个commit
。 此时diff
出来的commit
可能会包含其他人的代码,如果之前的代码没有lint
,此时就需要自己lint
了。如果上线流程是先合并
master
,那么上线的时候master
已经有了自己的代码,此时上线分支和master
就没有任何diff
了,所以也就起不到卡控的作用了。卡控分支前
n
天的commit
。理想情况下,前
n
天只包含自己的commit
和已经lint
过的commit
,merge master
的commit
可以自动过滤掉,因此可以很好的对新加的代码进行卡控。当然还是无法完全避免遇到别人没有
lint
过的代码,此时还是需要自己进行修复了。具体逻辑可以参考这个 node 包。
不管是哪种方法,因为是在老项目引入的 lint
,前期如果在流水线加 lint
卡控的话一定会遇到明明不是自己代码,却被 lint
卡控拦截的情况。
我个人看法是流水线 lint
其实不加也可以,如果编辑器自动修复添加了、commit
卡控也添加了,这已经足够了,如果真有人通过 -n
绕过卡控,那肯定是有理由的,也没必要走流水线再卡控。
上线
因为老项目中会有大量的不符合 eslint
规则的代码,因此上线有两种方案。
本地进行全量文件的
eslint --fix
后上线:优点:未来开发时原有文件的
lint
问题不用关心,开发者只需关注原有error
和自己当前的lint
问题即可。缺点:由于改动文件数较多,
eslint
不可完全信任,贸然上线可能会造成线上问题。仅仅上线
eslint
的卡控和保存时自动lint
的配置:优点:未改动代码逻辑,不会存在引发线上问题的隐患。
缺点:当开发者修改、保存老文件后,会自动触发
lint
修复,从而污染混淆本身的修改,增加后续code review
工作负担。
我是偏向于第 2
个方案的,虽然 eslint
自动修复一般不会引起问题,但程序肯定是不能 100%
相信的,如果造成了线上问题反而得不偿失。
如果采用第 2
个方案,后续开发老页面保存的时候一定会出现大面积的自动 lint
,我们可以在添加新代码前先保存一下触发 lint
并且提交一个 msg
为 lint auto fix
的 commit
。这样做有两个好处:
- 后续其他人遇到问题代码排查的时候看到
lint fix
就知道了这行代码不是你写的,他需要再往前找一个commit
的提交人。 - 过
pr
的时候我们可以按commit
看,第一个lint
的commit
如果没什么问题可以直接跳过,减轻cr
的负担。
总
在业务迭代繁忙的时候,想在老项目中引入 eslint
其实还挺难的,毕竟业务价值很难讲清楚,一个反向逻辑就是现在项目没有 eslint
也运行的好好的,但加入 eslint
有什么收益呢?
另一方面,当有人推动项目 eslint
的规则的时候仅仅添加规则和卡控,其他的步骤不去推动,当越来越多人遇到需要手动修复 eslint
或者因为 eslint
的问题被卡控提交,内心就会不断地增加对 eslint
的抗拒。
在安装相关插件、node
包的时候需要注意下版本号,找到匹配自己包的版本号的配置,不然可能会遇到配置了但不生效的问题。
当有新项目开发的时候,一定要把 eslint
的自动修复、相关配置都搞好,这样开发的时候也舒服,未来也不用再进行 eslint
的治理了。
未来也可以结合平时开发的经验和发生的线上问题,逐步完善 eslint
中的 rules
规则,使得项目代码质量越来越高。