授权转载自:nowThen
https://juejin.cn/post/6895889063111294990
前言
团队多人协同开发项目中困恼团队管理一个很大的问题是:无可避免地会出现每个开发者编码习惯不同、代码风格迥异,为了代码高可用、可维护性, 如何从项目管理上尽量统一和规范代码呢?
- [x] 文档约定 - 谆谆教导,自求多福?
- [x] 经常性CodeRevice - 苦口婆心,耳提面命?
显然这种无法实时反馈、延迟解决的方式会造成沟通成本高,往往最终结果还不太理想...
理想的方式是在项目工程化层面 借助可灵活配置的工具,自动化 解决。
而于此对应的,
细心的你,如果仔细观察,会发现无论是开源项目还是成熟的团队项目,打开项目源码,你会发现根目录下出现了越来越多的配置文件,这是前端项目在快速演变、逐渐完善健壮的一种表现。而对于我们来说,傻傻分不清楚可不行。
今天,我们就来分析一下跟编码风格、代码规范相关的、、这几个常见配置功能。
借助于EditorConfig
+Prettier
+ESLint
的组合,项目中通过统一约定配置,可以在团队成员在代码开发过程中就检查、约束、美化代码,统一编码风格;且可以省去很多的沟通成本,提前暴露代码缺陷,减少后期二次修改代码的风险;
简单归纳:
- EditorConfig: 跨编辑器和IDE编写代码,保持一致的简单编码风格;
- Prettier: 专注于代码格式化的工具,美化代码;
- ESLint:作代码质量检测、编码风格约束等;
当然,他们各有适用范围,接下来,我们就来一探究竟,重点关注ESLint。
EditorConfig
EditorConfig有助于从事同一项目的多个开发人员在跨多个编辑器和IDE使用时保持一致的编码风格。
EditorConfig项目包含一个用于定义编码样式的文件格式和一个文本编辑器插件集合,这些文本编辑器插件使编辑器可以读取文件格式并遵循定义的样式。
解读
- 依赖编辑器IDE的支持
某些编辑器已默认集成对EditorConfig的支持,比如常用的:Webstorm、IntelliJ IDEA等;
而另一些编辑器则需要借助安装对应的插件来支持:比如 Visual Studio Code、Atom等;
我们常用的Visual Studio Code安装的必装插件:
- 支持多种文件格式
编辑器读取到文件格式会匹配并遵循配置文件定义的规则;
- 就近原则
打开文件时,EditorConfig插件会在打开的文件的目录中以及每个父目录中查找名为.editorconfig
的文件。如果到达根文件路径或找到root = true的EditorConfig文件,将停止对.editorconfig
文件的搜索。
离文件最近的配置规则生效,优先级更高;一般在根目录设置一个配置文件即可。
- 配置文件
.editorconfig
定义规则配置,来避免常见的代码格式不一致和丑陋的 diffs。例如常见配置项:
# http://editorconfig.org
root = true
# 说明
## 设置文件编码为 UTF-8;
## 用两个空格代替制表符;
## 在保存时删除尾部的空白字符;
## 在文件结尾添加一个空白行;
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[Makefile]
indent_style = tab
复制代码
更多内容可以访问官网查看。
当然,我们看到EditorConfig包含的内容比较少,主要是配置我们的编辑器,编写代码时的简单规则,不足以满足项目更多需求;
Prettier
Prettier是一个诞生于2016年就迅速流行起来的专注于代码格式化的工具。出道即巅峰啊-.-Prettier
只关注格式化,并不具有lint检查语法等能力。它通过解析代码并匹配自己的一套规则,来强制执行一致的代码展示格式。
它在美化代码方面有很大的优势,配合ESLint可以对ESLint格式化基础上做一个很好的补充。
那么如何使用呢?
- 单独使用,配合编辑器IDE作代码格式化;
- 与ESLint等配合使用;在下文ESLint中详细谈,此处不予赘述;
1. 单独使用,配合编辑器IDE作代码格式化
以VSCode为例,首先安装Prettier插件。VSCode内置的代码格式化工具可以指定为由Prettier接管,此时右下角会显示为Prettier。可以自行配置格式化触发机制:换行时格式化、保存文件时格式化、还是自行快捷键触发;
本人的使用习惯是用快捷键手动触发格式化。
当在编辑器里格式化未生效时,可以在.settings.json
里检查对应文件格式指定的格式化程序并调整就可以:这样在VSCode编辑器里,触发文件格式化时就能根据配置自动美化格式代码;
配置项:
可以在VSCode 首选项-设置-扩展
或.settings.json
中更改通用配置;
当然还可以在具体项目根目录设置.prettierrc
单独配置;
比如以下一些配置:
{
// 设置强制单引号
"singleQuote": true,
// 为多行数组的非末尾行添加逗号 es5的对象,数组等
"trailingComma": "es5",
// 每行最大宽度 100
"printWidth": 100,
// 设置语句末尾不加分号
"semi": false,
// jsx中使用单引号
"jsxSingleQuote": true,
}
复制代码
最后格式化的生效策略同样是就近原则,一步步匹配目标文件最近父目录的配置文件,越近优先级越高。
ESLint
ESLint 是一个在 JavaScript 代码中通过规则模式匹配作代码识别和报告的插件化的检测工具,它的目的是保证代码规范的一致性和及时发现代码问题、提前避免错误发生。
ESLint 的关注点是代码质量,检查代码风格并且会提示不符合风格规范的代码。除此之外 ESLint 也具有一部分代码格式化的功能。
我们跟着ESLint官网的说明,来理解ESLint。
Lint发展历程
ESLint最初是由Nicholas C. Zakas ( JavaScript 高级程序设计 作者)于2013年6月创建的开源项目。它的目标是提供一个插件化的javascript代码检测工具。
JavaScript发展历程中出现的Lint工具:JSLint->JSHint->ESLint/TSLint;
- JSLint是最早出现的 Lint 工具,不支持灵活拓展及配置,必须接受它所有规则;
- JSHint 在 JSLint 的基础上提供了一定的配置项,给了开发者较大的自由,但无法添加自定义规则;
- Zakas创建ESLint的初衷就是觉得当时的JSHint存在局限性,无法添加自定义规则。
- ES6的出现后则让ESLint迅速大火。
因为ES6新增了很多语法,JSHint 短期内无法提供支持,而 ESLint 只需要有合适的解析器以及拓展校验规则 就能够进行 Lint 检查。此时babel就为兼容ESLint开发了 babel-eslint解析器,提供支持的同时也让ESLint成为最快支持 ES6 语法的 Lint 工具。
关于TSLint(已停止维护)
使用过TypeScript的童鞋对于TSLint应该不会陌生,它是由TypeScript团队推出并维护的。
但自2019 年 1 月起,根据 TSLint 的官方声明,TSLint 正在慢慢被废弃,并会逐步迁移到 ESLint作为代码检查的工具。
至于停止维护的原因:一是ESLint社区更活跃、越来越完善,且社区对ESLint的拥护声浪越来越高,相反TSLint则完善度不够;二是在持续迭代、支持新特性的过程中发现TSLint 的规则运作方式存在架构性的性能问题,相反的 ESLint 则具有更高效能的架构。
不过不得不感慨一句:即使官方已声明停止更新很长时间了,你会发现还是有很多TypeScript项目采用TSLint作为代码检查的工具,未做迁移。
如果你的项目还在使用TSLint,为了项目的长期维护性和获得更好的ts代码检查使用体验,尽快迁移至ESLint;
下图为JSHint、TSLint、ESLint、Prettier的Npm包下载量对比图:
那么ESLint出现的意义是什么?
ESLint官网的说明:代码检查是一种静态的分析,常用于寻找有问题的模式或者代码,并且不依赖于具体的编码风格。对大多数编程语言来说都会有代码检查,一般来说编译程序会内置检查工具。
JavaScript 是一个动态的弱类型语言,在开发中比较容易出错。因为没有编译程序,为了寻找 JavaScript 代码错误通常需要在执行过程中不断调试。像 ESLint 这样的可以让程序员在编码的过程中发现问题而不是在执行的过程中。
与Java等编程语言不同,JavaScript作为弱类型的动态语言,因为缺少编译阶段,有些本可以在编译过程中发现的错误,只能等到运行时才发现,这给我们调试和提前发现隐藏问题增加了一些难度,而 Lint 工具相当于为js增加了编译过程,在代码部署运行前进行静态分析,找到出错的地方和不规范的代码。
那么 TypeScript 已经能够在编译阶段检查出很多问题了,为什么还需要Lint工具代码检查呢?
因为 TypeScript 关注的重心是类型的检查,而不是代码风格。
总结一下ESLint的作用及优势:
- 检查语法错误,避免低级bug;
比如:api语法错误、使用了未定义的变量、修改const变量
- 统一团队代码风格
比如:使用tab还是空格,使用单引号还是双引号等
- 确保代码遵循最佳实践
比如:可以借助
eslint-config-standard
配置包扩展社区中流行的最佳实践的风格指南。
这样就能极大提高项目中多人协作开发时的效率、代码的可读性以及可维护性。
ESLint特点
一、ESLint 的所有规则都被设计成可插拔的
每条校验规则都是独立的,可以单独开启或关闭(没有什么可以被认为“太重要所以不能关闭”),还可以将结果设置成警告或者错误。
在规则编写时,每个规则都是单独的文件和对应的格式化方法。
二、ESLint是完全可配置的
ESlint 被设计为完全可配置的,除了规则可插拔,还可以编写自定义规则、引入社区规则配置集、插件等,让ESLint更契合每个项目的具体需求情况;
通过 eslint-plugin-react
配置包扩展支持React语法;
通过@typescript-eslint/parser
解析器支持typeScript语法及校验等;
三、ESLint 使用 Node.js 编写
在前端项目中便于安装且有一个快速的运行环境;
减轻了开发者编写自定义规则的门槛;
四、ESLint解析时将源码先转换成AST
ESLint 使用 Esprima 将源代码解析成 AST来分析代码中的模式,再通过匹配规则定义识别和报告搜集的代码信息。
虽然多转换一层效率略微降低,好处是可以支持使用任意规则来检测 AST 是否符合预期,这使得 ESLint 高可扩展性。
支持的配置文件格式
ESLint 支持几种格式的配置文件:
- JavaScript - 使用
.eslintrc.js
然后输出一个配置对象。 - YAML - 使用
.eslintrc.yaml
或.eslintrc.yml
去定义配置的结构。 - JSON - 使用
.eslintrc.json
去定义配置的结构,ESLint 的 JSON 文件允许 JavaScript 风格的注释。 - (弃用) - 使用
.eslintrc
,可以使 JSON 也可以是 YAML。 - package.json - 在
package.json
里创建一个eslintConfig
属性,在那里定义你的配置。
如果同一个目录下有多个配置文件,ESLint 只会使用一个。优先级顺序如下:
.eslintrc.js
.eslintrc.yaml
.eslintrc.yml
.eslintrc.json
.eslintrc
package.json
遇到项目内有多个层叠配置时,依然采用就近原则作为高优先级;
配置文件说明
Rules-启用的规则及其各自的错误级别
ESLint 附带有大量的规则。你可以使用注释或配置文件修改你项目中要使用的规则。要改变一个规则设置,你必须将规则 ID 设置为下列值之一:
"off"
或0
- 关闭规则"warn"
或1
- 开启规则,使用警告级别的错误:warn
(不会导致程序退出)"error"
或2
- 开启规则,使用错误级别的错误:error
(当被触发的时候,程序会退出)
Globals-配置额外的全局变量
启用ESLint规则后,当访问当前源文件内未定义的变量时,no-undef 规则将发出警告。
而有时候,我们是需要在其他文件访问一些全局变量的,且保证能正常取到值。这时可以在 ESLint 中定义这些全局变量,这样 ESLint 就不会发出警告了。
- 用注释指定全局变量,格式如下:
/* global var1, var2 */
复制代码
这定义了两个全局变量,var1
和 var2
。如果你想选择性地指定这些全局变量可以被写入(而不是只被读取),那么你可以用一个 "writable"
的标志来设置它们:
/* global var1:writable, var2:writable */
复制代码
- 配置文件中通过
globals
配置属性设置,对于每个全局变量键,将对应的值设置为"writable"
以允许重写变量,或"readonly"
不允许重写变量。例如:
// .eslintrc.js
"globals": {
"var1": "writable",
"var2": "readonly"
}
复制代码
Environments - 指定脚本的运行环境
每种环境都有一组特定的预定义全局变量。如brower、node环境变量、es6环境变量等。
'env': {
'browser': true,
'commonjs': true,
'es6': true
},
复制代码
Plugins - 第三方插件
ESLint 支持使用第三方插件,先在项目中下载安装要引入的插件,配置文件中使用 plugins
关键字来存放插件名字的列表。插件名称可以省略 eslint-plugin-
前缀。
plugins: ['react', 'babel'], // eslint-plugin-react eslint-plugin-babel
复制代码
Extends - 继承
一个配置文件可以被基础配置中的已启用的规则继承。
extends: ["eslint:recommended","plugin:prettier/recommended"],
复制代码
配置代码注释方式
有时候,我们需要在代码中忽略ESLint的某些规则检查,此时我们可以通过加入代码注释的方式解决:可以指定整个文件、某一行、某一区块开启/关闭 某些或全部规则检查;
/* eslint-disable */ --禁用全部规则 放在文件顶部则整个文件范围都不检查
/* eslint-disable no-alert, no-console */ --禁用某些规则
// eslint-disable-line --当前行上禁用规则
// eslint-disable-next-line --下一行上禁用规则
复制代码
具体参考:eslint.bootcss.com/docs/user-g…;
使用ESLint
安装 ESLint
ESLint 可以安装在当前项目中或全局环境下,但因项目间存在的差异性,我们一般会将它安装在当前项目中。安装:
yarn add --save-dev eslint
复制代码
安装插件和解析器
假如项目中使用了TypeScript和React,则安装:
// 我们需要安装 @typescript-eslint/parser,替代掉默认的Espree解析器。
yarn add --save-dev typescript @typescript-eslint/parser
// 安装eslint-plugin-react配置包扩展支持React语法;安装@typescript-eslint/eslint-plugin提供额外的ts 语法的规则
yarn add --save-dev eslint-plugin-react @typescript-eslint/eslint-plugin
复制代码
其他的插件和解析器请根据实际项目需要安装。
创建配置文件
我们在项目的根目录下创建一个 .eslintrc.js
,内容如下:
module.exports = {
parser: '@typescript-eslint/parser',
plugins: ['react','@typescript-eslint'],
rules: {
// 禁止使用 var
'no-var': "error",
// 优先使用 interface 而不是 type
'@typescript-eslint/consistent-type-definitions': [
"error",
"interface"
]
}
}
复制代码
站在巨人的肩膀上使用
前端社区中有很多比较好的规则集,我们要做的是站在巨人的肩膀上,基于已有规则集,构建适合自己及团队的规则配置。
通过研究他人优秀的规则集,慢慢地构建自用或公司的规则配置;
本篇文章介绍的ESLint只是涉及的一些重要的概念及基本使用。ESLint规则及配置包含了很多的内容,想要用的好,值得花精力自行好好研究。
Q&A
1. 如何方便地开始使用ESLint,而且尽量不改动以前的代码?
如果你正在使用GIt做项目代码管理,那么则可以借助husky + lint-staged + Prettier 在Git提交时,自动强制校验并格式化且修复代码,而且只处理自己本次改动提交的文件。
采用这种pre-commit阶段增量校验的模式,尽量避免对老旧代码的影响;这种方式可以稳健地逐步完善老项目;
2. 如何解决Prettier与ESLint的配置冲突问题?
在代码格式化时采用Perttier规则,而我们代码校验使用的是ESLint,如果同一个规则配置不一致,往往就会出现冲突问题;
比如:字符串单、双引号的配置,eslint fix后把字符串变成单引号,再次编辑文件后,保存(Prettier)自动格式化后却又变成双引号,导致代码校验异常。
解决方式一:要么修改 eslintrc,要么修改 prettierrc 配置,让它们配置保持一致;
解决方式二:禁用 ESLint中和Prettier配置有冲突的规则;再使用 Prettier 来替代 ESLint 的格式化功能;
安装eslint-config-prettier
插件配置集,把其配置到eslintrc规则的尾部。执行ESLint命令,会禁用那些和Prettier配置有冲突的规则。
安装eslint-plugin-prettier
插件,先使用Prettier对代码进行格式化,再并对不一致的地方进行标记;
这两个包配合使用,可以达到运行 eslint \--fix
时,采用Prettier的配置规则 来格式化文件。
具体配置及使用方式,请自行查阅探索;
总结
ESLint、Prettier、EditorConfig的引入,需要牺牲一些开发人员的编码自由,来保证团队成员代码风格的一致性,进而提高代码的可读性、可维护性。使项目更好管理,成员之间合作更顺畅。
就算不从团队开发考虑,个人从中也能逐渐建立良好的开发规范,对于自己的成才也是长久的。
当然,我们也该清楚地认识到工具的局限性:
一、清楚定位:
ESLint等解决的是团队开发规范的问题,并不能解决其他诸如编码能力、代码合理性等问题, 还属于工程化中比较弱的一环。
有时会遇到团队制定特别严格的规则校验,且在每次代码提交时,收集检查结果作为代码质量评估的重要指标。个人认为这种方式固然可以量化一部分代码质量考核问题 ,但形式主义过重。不过也是廖胜于无的做法。
- 太过严格的规则,限制了代码的灵活性。每一个规则都应该是可被讨论,具体开启与否应该视团队而定;
- 语言或框架某个写法如果是被严禁使用的,那它就应该在源头被消灭;之所以存在肯定有一定的意义的;
- ESLint不是神药,最佳代码实践往往在于多多探索,持续更新;
二、技术革新快速,之前认为的准则不一定就适用于当下,要保持持续调整的心态和跟进优化的行动力;
三、不要作严格代码规范的强迫症患者, 它并不是目的,只是一个让我们更方便管理项目,从复杂团队项目解脱出来的一个方式。
更倾向的做法是:不要完全依赖工具的规则校验,要让它们帮忙我们养成良好的编码习惯,培养代码质量意识,指引我们写出更优的代码,而不是依赖它