大家好,我是若川。持续组织了近一年的源码共读活动,感兴趣的可以 点此扫码加我微信 ruochuan12 参与,每周大家一起学习200行左右的源码,共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试系列。另外:目前建有江西|湖南|湖北
籍前端群,可加我微信进群。
背景
在日常需求迭代中,代码的规范与质量是编码的重要一环。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
文件引入相应的规则。
module.exports = {
extends: ['alloy', 'alloy/vue'],
env: {
// 你的环境变量(包含多个预定义的全局变量)
},
globals: {
// 全局变量
moment: 'readonly',
},
rules: {
// 自定义你的规则 0-关闭,1-warn,2-error
'vue/component-tags-order': 0,
'vue/no-deprecated-slot-attribute': 0,
'vue/no-deprecated-slot-scope-attribute': 0,
'vue/no-duplicate-attributes': [
'error',
{
allowCoexistClass: true,
allowCoexistStyle: true,
},
],
'no-param-reassign': 1,
'no-console': 1,
'no-magic-numbers': 1,
'default-case': 2,
},
};
如上所示,我们可以在 rules
中定义或者覆盖一些规则。
Prettier
Prettier
是一个代码格式化工具,相比于 eslint
中的代码格式规则,它提供了更少的选项,却更加专业。
image-20220923090302423
相比于 eslint
, Prettier
主要格式样式相关的,比如有没有分号、空格数、一行最大字符数等等,而 eslint
通过解析出代码的 AST
,可以自动格式化或者检测出一些潜在的问题,比如是否允许使用 console
、变量声明但未使用、switch
缺少 defaut
等。
当然 eslint
也可以配置样式相关的规则,但存在一些情况 eslint
无法胜任,因此格式化相关的我们都交给更专业的 Prettier
,安装 Prettier
的 node
包,并且根目录增加配置文件 .prettierrc.js
。
// .prettierrc.js
module.exports = {
// 一行最多 120 字符
printWidth: 120,
// 使用 4 个空格缩进
tabWidth: 4,
// 不使用缩进符,而使用空格
useTabs: false,
// 行尾需要有分号
semi: true,
// 使用单引号
singleQuote: true,
// 对象的 key 仅在必要时用引号
quoteProps: 'as-needed',
// jsx 不使用单引号,而使用双引号
jsxSingleQuote: false,
// 末尾需要有逗号
trailingComma: 'all',
// 大括号内的首尾需要空格
bracketSpacing: true,
// jsx 标签的反尖括号需要换行
bracketSameLine: false,
// 箭头函数,只有一个参数的时候,也需要括号
arrowParens: 'always',
// 每个文件格式化的范围是文件的全部内容
rangeStart: 0,
rangeEnd: Infinity,
// 不需要写文件开头的 @prettier
requirePragma: false,
// 不需要自动在文件开头插入 @prettier
insertPragma: false,
// 使用默认的折行标准
proseWrap: 'preserve',
// 根据显示样式决定 html 要不要折行
htmlWhitespaceSensitivity: 'css',
// vue 文件中的 script 和 style 内不用缩进
vueIndentScriptAndStyle: false,
// 换行符使用 lf
endOfLine: 'lf',
// 格式化内嵌代码
embeddedLanguageFormatting: 'auto',
};
编辑器自动修复
这一步我认为是推动 eslint
最重要的一步,大家抗拒项目添加 eslint
一个很大的原因就是本地没有开启实时检查和自动修复,当提交 commit
的时候遇到 eslint
规则卡控就很难受了。
团队内都使用的 VSCode
进行开发,可以安装 Eslint
和 Prettier
插件。
image-20220925113210716
image-20220925113256543
在本地新增 .vscode/settings.json
文件进行插件的配置,并且该文件不忽略 git
,所有人共享。
{
"editor.defaultFormatter": "esbenp.prettier-vscode", // 用 Prettier 格式化
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true // 保存时自动进行 eslint 的修复
},
"editor.formatOnSave": true // 保存时自动格式化
}
这个文件是 VSCode
针对当前工程的配置,配置后保存文件的时候插件会自动帮助我们格式化,同时有实时的错误提示。
Kapture 2022-09-25 at 15.41.13
这里需要注意的一点是,保存的时候会同时进行 prettier
和 eslint
的修复,如果 eslint
也配置了样式相关的规则,此时可能发生冲突,导致自动格式化后会有 eslint
的报错,此时可以将相应的 eslint
规则关闭。
commit 卡控
为了保证 eslint
规则的有效,需要在提交 commit
的时候进行检查,如果存在没有修复的 eslint
问题直接终止提交。
直接使用 "husky": "^1.3.1"
和 "lint-staged": "^8.1.5"
两个 node
包,需要注意下版本号,最新的配置有些不同了,下边是该版本下的配置。
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"linters": {
"src/**/*.{js,vue}": [
"eslint --fix",
"git add"
],
"src/**/*.{js,vue,html,css,scss,sass}": [
"prettier --write",
"git add"
]
}
},
husky
提供了 pre-commit
的钩子,然后 lint-staged
对暂存区代码自动进行格式化,如果出错的话会直接退出。
这样当我们提交 commit
的时候就会运行 eslint
和 prettier
进行代码的格式化。
image-20220925162909689
流水线卡控
虽然上一步对 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 包。https://github.com/wyntau/lint-recently#readme
不管是哪种方法,因为是在老项目引入的 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
规则,使得项目代码质量越来越高。
我在阿里招前端,我该怎么帮你?(现在还可以加模拟面试群) 如何拿下阿里巴巴 P6 的前端 Offer 如何准备阿里P6/P7前端面试--项目经历准备篇 大厂面试官常问的亮点,该如何做出? 如何从初级到专家(P4-P7)打破成长瓶颈和有效突破 若川知乎问答:2年前端经验,做的项目没什么技术含量,怎么办?
如何准备20K 的大厂前端面试
················· 若川简介 ·················
你好,我是若川,毕业于江西高校。现在是一名前端开发“工程师”。写有《学习源码整体架构系列》20余篇,在知乎、掘金收获超百万阅读。 从2014年起,每年都会写一篇年度总结,已经坚持写了8年,点击查看年度总结。 同时,最近组织了源码共读活动,帮助4000 前端人学会看源码。公众号愿景:帮助5年内前端人走向前列。
扫码加我微信 lxchuan12、拉你进源码共读群
今日话题
目前建有江西|湖南|湖北 籍 前端群,想进群的可以加我微信 lxchuan12 进群。分享、收藏、点赞、在看我的文章就是对我最大的支持~