eslint
作用
eslint本质就是一个内置有解析器的工具,它可以将项目代码解析成AST,然后根据AST抽象语法树分析出代码里存在的问题然后给出警告或者报错。eslint的初衷就是在代码编写阶段尽早得发现存在的错误;除了语法检查外,eslint也具有一定的代码格式化能力,但是不是其能力的重心(prettier在代码格式方面更加专业)。
初始化
如果使用脚手架初始化项目,比如通过vite
创建项目:pnpm create vite
这样得到的项目模板都对eslint进行了初始化配置。
如果手动给项目配置eslint检查:
bash
# 全局安装eslint依赖
npm i eslint -g
# 给项目初始化eslint,包括安装devDependencies依赖 & 生成配置
eslint --init
经过问答之后生成eslint的配置文件.eslintrc.cjs
配置项------parser & parserOptions
本身eslint的语法检查就是一个先对代码进行静态解析得到AST,然后再判断的过程。所以在eslint默认的解析器基础上,自然需要一些更高级的解析器来支持更新的语法以及语言,比如eslint的默认解析器是Espree
,它只支持对es5的js进行解析,所以我们如果项目中使用了ts,Espree
就不行了,自然需要用到更高级的(支持ts以及最新es版本的)解析器@typescript-eslint/parser
。
更加准确的说,在配置文件中parser
项的value
为所使用解析器的模块名,如
javascript
module.exports = {
"parser": "@typescript-eslint/parser", // 使用@typescript-eslint/parser这个解析器进行语法解析
// ...
}
通过parser
我们指定了项目所使用的语法解析器,parserOptions
就相当于给出解析器更详细的解析配置,比如如下配置,parserOptions
就具体指定了@typescript-eslint/parser
解析器应该支持最新版本的es标准("ecmaVersion": "latest"
)以及项目的模块化标准为esModule
("sourceType": "module"
)
javascript
module.exports = {
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
}
配置项------globals & env
globals是定义全局变量,规定他们可不可以重写以及是否被禁用,env就是一组globals预设(一组打包的globals配置),如下配置:
js
{
"globals": {
// 声明 jQuery 对象为全局变量
"$": false, // true表示该变量为 writeable,而 false 表示 readonly, 即不可重写$
"jQuery": false,
"Promise": "off" // 禁用Promise
},
"env": {
"es6": true // 启用es6这个预设,里面肯定包含了若干globals配置来对es6进行支持
},
}
配置项------rules & plugins & extends
rules就是具体的eslint进行语法检查所依据的规则;但是官方只提供了关于标准js的检查规则,所以需要plugins来拓展规则集合,比如针对用Vite创建的react + Ts的项目,就通过react-refresh
这个plugin拓展了eslint的规则,但plugins中拓展的规则默认不开启,所以plugins要与rules配合使用来拓展eslint的功能;但是我们总不可能把所有规则一条一条的写在rules中,extends就相当于一组配置好的rules与plugins组合,解决了这个痛点。
rules中的规则优先级最高,会覆盖拓展以及插件中引入的规则。
一般来说规则的值有三个值,只需控制是开启还是关闭:
off
或 0:关闭规则warn
或 1:开启规则,warn
级别的错误 (不会导致程序退出)error
或 2:开启规则,error
级别的错误(当被触发的时候,程序会退出)
如"eqeqeq": "off"
。有的规则有自己的属性,使用起来像这样:"quotes": ["error", "double"]
。具体内容查看规则文档。
文件级别的配置优先级
我们的项目中可以存在多个eslint配置文件,那么文件进行eslint检查时,文件所处位置向上直至文件系统的根目录路径上所有的eslint配置文件都会生效,但是越"靠近"文件的配置优先级越高(可以理解为高优先级规则覆盖低优先级规则)。
如下,source.js
使用配置A,但是test.js
使用配置B和配置A,但是配置B中的规则会覆盖掉A中相同的规则。
css
your-project
├── .eslintrc - eslint配置A
├── lib
│ └── source.js
└─┬ childFolder
├── .eslintrc - eslint配置B
└── test.js
配置项"root": true
可以阻止继续递归的查找比较远的根目录。
package.json
中也可以对eslint进行配置,所以项目中文件的eslint配置文件可以总结为:
- 与要检测的文件在同一目录下的
.eslintrc.*
或package.json
文件 - 继续在父级目录寻找
.eslintrc
或package.json
文件,直到根目录(包括根目录)或直到发现一个有"root": true
的配置。
vscode中的使用
在vscode中安装eslint插件之后,无需在命令行中手动执行eslint命令即可在编码时实时提供eslint语法检查,而且也可以开启eslint的代码格式化功能,需要进行如下vscode配置:
ctrl + shift + p
打开搜索栏搜索settings.json
配置文件,项目内生成.vscode
文件夹,在其下的settings.json
中新增配置:
json
{
"[typescriptreact]": {
"editor.formatOnSave": true
},
}
意为对tsx语言进行保存时格式化。
支持的语言:
ini
javascript;
javascriptreact;
typescript;
typescriptreact;
json;
graphql;
prettier
作用
prettier的作用就是代码格式化,可以理解为prettier把代码解析之后再按照预期的规则进行重新打印,并且支持各种语言,如JavaScript
、 Flow
、 TypeScript
、 CSS
、 SCSS
、 Less
、 JSX
、 Vue
、 GraphQL
、 JSON
、 Markdown
等等
初始化 & prettier执行
pnpm i prettier -D
安装之后,工程目录下新建.prettierrc.js
配置文件以及.prettierignore
忽略文件,这样就相当于告诉我们的工程我们使用了prettier,之后执行prettier
格式化命令时即按照配置进行格式化,如npx prettier. --write
格式化所有文件。
prettier 隐式忽略node_modules
,并不需要将其添加到.prettierignore
中
.prettierrc.js
配置就比较简单直观了,都是一条一条具体的格式化规则,如下配置中基本都是默认值:
js
//此处的规则供参考,其中多半其实都是默认值,可以根据个人习惯改写
module.exports = {
printWidth: 80, //单行长度
tabWidth: 2, //缩进长度
useTabs: false, //使用空格代替tab缩进
semi: true, //句末使用分号
singleQuote: true, //使用单引号
quoteProps: 'as-needed', //仅在必需时为对象的key添加引号
jsxSingleQuote: true, // jsx中使用单引号
trailingComma: 'all', //多行时尽可能打印尾随逗号
bracketSpacing: true, //在对象前后添加空格-eg: { foo: bar }
jsxBracketSameLine: true, //多属性html标签的'>'折行放置
arrowParens: 'always', //单参数箭头函数参数周围使用圆括号-eg: (x) => x
requirePragma: false, //无需顶部注释即可格式化
insertPragma: false, //在已被preitter格式化的文件顶部加上标注
proseWrap: 'preserve', //不知道怎么翻译
htmlWhitespaceSensitivity: 'ignore', //对HTML全局空白不敏感
vueIndentScriptAndStyle: false, //不对vue中的script及style标签缩进
endOfLine: 'lf', //结束行形式
embeddedLanguageFormatting: 'auto', //对引用代码进行格式化
};
vscode中的使用
安装prettier的vscode插件,安装之后我们可以右键需要进行格式化的文件然后选择prettier进行格式化。
自动化:
ctrl + shift + p
打开搜索栏搜索settings.json
配置文件,项目内生成.vscode
文件夹,在其下的settings.json
中新增配置:
json
{
// 设置全部语言的默认格式化程序为prettier
"editor.defaultFormatter": "esbenp.prettier-vscode",
// 设置特定语言的默认格式化程序为prettier
// "[javascript]": {
// "editor.defaultFormatter": "esbenp.prettier-vscode"
// },
// 设置全部语言在保存时自动格式化
"editor.formatOnSave": true
// 设置特定语言在保存时自动格式化
// "[javascript]": {
// "editor.formatOnSave": true
// }
}
eslint && prettier
最佳实践------解决eslint与prettier在代码格式上的冲突
因为eslint本身也具备对代码格式的控制与检查能力,所以不可避免可能会与prettier的代码格式冲突,比如eslint配置rules
中对缩进的要求为2并在不满足时报错,.eslint.cjs
:
js
rules: {
indent: ['error', 2],
},
.prettierrc.cjs
:
js
module.exports = {
tabWidth: 6, //缩进长度
};
那么我们在执行npx prettier . --write
后进行eslint代码检查eslint .
就会把所有缩进问题进行报错,检查不通过。
解决方案很简单------思路就是把prettier的规则复写进eslint中,并对原本eslint中的格式配置进行覆盖,这样就做到了eslint的格式化检查与prettier的格式化行为统一。
用社区的轮子即可:
bash
# 安装eslint-config-prettier
pnpm install -D eslint-config-prettier
js
// 在 .eslintrc.* 文件里面的 extends 字段最后添加两行:
{
"extends": [
...,
"已经配置的规则",
+ "prettier",
]
}
extends
的值为数组,后面的数组项会继承和覆盖前面的配置,所以完成了prettier规则对eslint规则的扩充和覆盖。
最佳实践------省略prettier格式化命令,eslint进行格式化与检查一步到位
完成上述操作本质是做到了 ESLint
会按照 Prettier
的规则做相关校验,也就是说先执行Prettier格式化后再执行eslint检查不会因为格式问题冲突而报错,但是还是需要运行 Prettier
命令来进行格式化。为了避免多此一举,社区也提供了整合上面两步的方案:在使用 eslint --fix
(eslint错误修复) 时候,实际使用 Prettier
来替代 ESLint
的格式化功能。操作如下:
bash
# 安装eslint-plugin-prettier
pnpm install -D eslint-plugin-prettier
js
// 在 .eslintrc.* 文件里面的 extends 字段最后再添加一行:
{
"extends": [
...,
"已经配置的规则",
+ "plugin:prettier/recommended"
],
"rules": {
+ "prettier/prettier": "error",
}
}
这个时候运行 eslint --fix
实际使用的是 Prettier
去格式化文件。在rules
中添加"prettier/prettier": "error"
,用意是编写代码时不符合prettier格式规范的编码eslint直接自动报错(结合vscode的eslint插件实时报错的能力)。
当然直接执行eslint --fix
会没有反应,原因是eslint
命令缺少目标文件,可以用--ext [文件拓展名,[文件拓展名]]
的形式指定需要进行eslint修复以及检查的文件,比如react + ts项目中:
bash
eslint . --fix --ext ts,tsx --report-unused-disable-directives --max-warnings 0
表示进行eslint检查的同时进行自动修复(--fix),针对的文件是以ts、tsx为拓展名的(--ext ts,tsx),还有一些错误打印相关的要求(--report-unused-disable-directives),代码执行不退出的可以容忍的警告数量为0。
eslint命令参数参考
ini
eslint [options] file.js [file.js] [dir]
Basic configuration: //基本配置
--no-eslintrc Disable use of configuration from .eslintrc.* //禁止使用来自.eslintrc.*的配置文件
-c, --config path::String Use this configuration, overriding .eslintrc.* config options if present //如果存在.eslintrc.*,则使用且重写该配置文件
--env [String] Specify environments //指定环境
--ext [String] Specify JavaScript file extensions - default: .js //指定的JS文件扩展名,默认:.js
--global [String] Define global variables //定义全局变量
--parser String Specify the parser to be used //指定使用某种解析器
--parser-options Object Specify parser options //指定解析参数
Specifying rules and plugins: //指定规则和插件
--rulesdir [path::String] Use additional rules from this directory //从该路径使用额外的规则
--plugin [String] Specify plugins //指定插件
--rule Object Specify rules//指定规则
Fixing problems://修正问题
--fix Automatically fix problems//自动修复问题
--fix-dry-run Automatically fix problems without saving the changes to the file system//自动修复问题而不保存对文件系统的更改
Ignoring files: //忽略文件
--ignore-path path::String Specify path of ignore file //指定忽略的文件
--no-ignore Disable use of ignore files and patterns //禁止使用忽略文件和样式
--ignore-pattern [String] Pattern of files to ignore (in addition to those in .eslintignore) //要忽略的文件模式(除了在.eslintignore中的文件)
Using stdin: //unix]标准输入(设备)文件
--stdin Lint code provided on <STDIN> - default: false
--stdin-filename String Specify filename to process STDIN as //指定用于处理stdin的文件名
Handling warnings://处理警告
--quiet Report errors only - default: false //仅以错误报告出来
--max-warnings Int Number of warnings to trigger nonzero exit code - default: -1 //要触发非零退出代码的警告数-默认值:-1
Output:
-o, --output-file path::String Specify file to write report to //指定输出的文件路径
-f, --format String Use a specific output format - default: stylish //使用特定的输出格式-默认:stylish
--color, --no-color Force enabling/disabling of color
Inline configuration comments: //内联配置注释
--no-inline-config Prevent comments from changing config or rules //阻止注释更改配置或规则
--report-unused-disable-directives Adds reported errors for unused eslint-disable directives //为未使用的eslint disable指令添加报告的错误
Caching:
--cache Only check changed files - default: false //仅仅检查改变过的文件
--cache-file path::String Path to the cache file. Deprecated: use --cache-location - default: .eslintcache//缓存文件的路径。已弃用:使用--缓存位置-默认值:.eslintcache
--cache-location path::String Path to the cache file or directory//缓存文件或文件夹的路径
Miscellaneous://其他
--init Run config initialization wizard - default: false //运行配置初始化向导-默认值:false
--debug Output debugging information//输出调试信息
-h, --help Show help
-v, --version Output the version number
--print-config path::String Print the configuration for the given file//打印给定文件的配置
最佳实践------覆盖vscode本地格式化配置(代码格式层面协作统一)
由于每个人本地的 VS Code 代码格式化配置不拘一格,在实际的项目开发中,多多少少会因为格式化问题产生争议。因此需要有一个统一的规范覆盖本地配置,editorconfig for vs code
承担起了这个作用,只要在项目工程的根目录文件夹下添加.editorconfig
文件,那么这个文件声明的代码规范规则能覆盖编辑器默认的代码规范规则,从而实现统一的规范标准。
一般我们都用prettier进行代码格式化,在vscode中Prettier读取配置的优先级即:
Prettier
配置文件,比如.prettierrc
、.prettier.config.js
。.editorconfig
文件,用于覆盖用户/工作区设置。
.editorconfig
举例:
ini
root = true # 根目录的配置文件,编辑器会由当前目录向上查找,如果找到 `roor = true` 的文件,则不再查找
[*]
indent_style = space # 空格缩进,可选"space"、"tab"
indent_size = 4 # 缩进空格为4个
end_of_line = lf # 结尾换行符,可选"lf"、"cr"、"crlf"
charset = utf-8 # 文件编码是 utf-8
trim_trailing_whitespace = true # 不保留行末的空格
insert_final_newline = true # 文件末尾添加一个空行
curly_bracket_next_line = false # 大括号不另起一行
spaces_around_operators = true # 运算符两遍都有空格
indent_brace_style = 1tbs # 条件语句格式是 1tbs
[*.js] # 对所有的 js 文件生效
quote_type = single # 字符串使用单引号
[*.{html,less,css,json}] # 对所有 html, less, css, json 文件生效
quote_type = double # 字符串使用双引号
[package.json] # 对 package.json 生效
indent_size = 4 # 使用2个空格缩进
最佳实践------Husky + lint-staged优化git工作流
husky基本使用
husky做的事情就是在git
工作流的某个时机触发脚本,也就是git hook
,比如我们在git commit
之前进行eslint
语法检查,eslint检查过程中报错或者警告太多是会中断指令(git commit
)执行,所以这样就保证了提交到远程的代码是通过eslint检查的。
(配置详情查询husky官网)
安装依赖:
pnpm i -D husky
husky初始化:
csharp
pnpm dlx husky-init
不同包管理工具初始化的命令不同,去husky官网指南查看即可。
执行如上命令后,项目里生成.husky
文件夹,文件夹下有个pre-commit
文件,内容如下:
bash
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
pnpm lint
前两行不知道具体啥用,猜测应该是husky内部使用,下面写的命令pnpm lint
即为在commit
之前需要执行的命令。同时因为husky初始化命令的执行,package.json
的script
脚本中多了一个"prepare": "husky install"
,试了试用处不大,无心理会...
这里的git hook
已经满足当下需求了,其它工程化需要的husky
配置同理。
lint-staged
如果一个项目中期才引入husky,那么我们只想对git add
到暂存区的文件进行git hook
的脚本触发,那么可以借助lint-staged
来实现。