ESLint 这玩意儿用起来让人又爱又恨,一大堆配置能弄出一堆风格,稍不留神就整个红色波浪线恶心一下你,今天来好好瞧瞧这小可爱到底怎么配。
本文不讲解具体的规则内容,而是手把手告诉你 ESLint 有哪些配置项,都是什么意思,应该如何去配。建议点赞收藏,在开发项目时可以先看手册,再配合官方文档查看细节。
文件格式
常用的配置文件有 .js
和 .json
两种。其中,.js
的优先级更高。在 ESLint JSON 文件中,你也可以放心使用 JS 风格的注释(// 注释......),ESLint 会安全地忽略配置文件中的注释。
个人比较喜欢 .js
风格,可以在里面定义变量或者做一些额外的处理。
除了上述两种风格外,还有 cjs
、yaml
、yml
格式,甚至也可以直接在 package.json
中直接配置,有兴趣的可以参考配置文件。
配置对象
一套完整的 ESLint 文件大概长这样:
module.exports = {
root: true,
env: {},
globals: {},
extends: {},
parser: 'xxxxxx'
parserOptions: {},
plugins: [],
rules: {},
// ......
}
我们来介绍下这些常用的属性。
root 与层次结构
首先,ESLint 的配置文件可以有多份,允许放置在多个层次结构中。如果一个 .eslintrc
文件和一个被提示的文件在同一个目录下,那么该配置将被优先考虑(就近原则)。接着,ESLint 会继续沿着目录结构向上搜索,合并沿途发现的任何 .eslintrc 文件,直到到达 root: true
的 .eslintrc
文件或根目录。
也就是说,ESLint 会像贪吃蛇一样,从当前目录出发,一路吃掉沿途配置,所以,如要将这条蛇限制在一个特定的项目中,就需要在项目根层的 .eslintrc.*
中设置 root: true
。因为它一旦找到了 root,就会停止在父文件夹中寻找。
module.exports = {
root: true,
};
env 环境与变量
使用 env
指定环境,并通过将每个环境设置为 true
来启用想要的环境。
所以环境是什么?举个例子大家就一目了然了。
我们先在配置文件中添加一条规则:不允许使用未定义的变量。
module.exports = {
rules: {
'no-undef': 'error',
},
};
此时会发现这条规则对子自个儿报错了,真的是大义灭亲啊......
鼠标 hover 看下报错原因:
很明显,在 ESLint 看来 module
并没有被明确定义,所以触发了这条规则。但作为 Node 环境中内置的全局变量,它确确实实存在,所以我们必须在 env
中设置 node: true
来显示地告诉 ESLint 当前的运行环境为 Node。
module.exports = {
env: {
node: true
}
rules: {
'no-undef': 'error',
},
};
此时,ESLint 就会添加 Node.js 的全局变量,module 就不会报错了。于此相似的还有浏览器中的 window
变量。
比如,我们创建一个 test.js
文件,随便写点东西:
window.test = '123';
console.log(111);
会发现 ESLint 又报错了,原因也是变量未定义。
同理,我们需要在 env
中指定 浏览器的全局变量:
module.exports = {
env: {
node: true,
browser: true
}
rules: {
'no-undef': 'error',
},
};
ESLint 指定环境是为了避免我们潜意识中使用了与当前环境不符的变量,试想如果在浏览器环境中写了 Node.js 独有的语法,那么确实是会报错的。
另外,这些环境并不互斥,所以你可以一次定义多个环境。
globals 与 全局变量
globals
支持自定义扩展 env
中没有的全局变量。比如微信小程序中的 wx
全局变量,就需要我们自己添加了。
module.exports = {
globals: {
wx: 'writable', // readonly-只读 / writable-可改
},
rules: {
"no-undef": "error"
},
}
writable
表示该变量允许被修改和覆盖,如果你觉得这样有风险,也可以设置为 readonly
标为只读。
rules 规则
ESLint 有大量的内置规则,当 ESLint 报错时,个人建议是先根据报错提示跳转到官网查看下规则,再决定是否需要修改。
规则的严重程度
一个规则可以指定三种严重程度
属性值 | 严重程度 | 说明 |
---|---|---|
error 或 2 | 错误 | 当使用 ESLint CLI 时,错误导致 CLI 以非零代码退出 |
warn 或 1 | 警告 | 当使用 ESLint CLI 时,在不改变退出代码报告警告内容。如仅报告警告内容,则退出代码为 0 |
off 或 0 | 无 | 彻底关闭规则 |
除严重程度外,如果规则有额外的选项,可以使用数组在后面进行追加,比如:
{
"rules": {
"quotes": ["error", "double"]
}
}
quotes
数组的第一个元素表示严重程度为 错误
,第二个元素表示 双引号
,说明必须使用双引号,否则 ESLint 会报错。
有些规则会有多个配置项,这时可以根据文档的说明,继续往后添加自己需要的options。比如 operator-linebreak
,它会为运算符强制执行一致的换行样式:
{
"rules": {
'operator-linebreak': [
'warn',
'after',
{
overrides: {
'?': 'before',
':': 'before',
},
},
],
}
}
参数一
: 警告级别的严重程度;
参数二
:要求在运算符之后换行;
参数三
:是一个对象,其中的 overrides
可以覆盖全局的规则,针对个别运算符单独配置。
所以基于该换行规则的正确写法就成了这样:
foo = 1 + 2;
// 或者在运算符后面换行
foo = 1 +
2;
// 三元运算符则需要在 '?' 和 ':' 之前换行
answer = everything
? 42
: foo;
这些就是规则的基本配置。我们也可以直接使用插件内的规则,详情参见 plugins 插件 章节。
extends 扩展配置
配置 extends 后,就可以继承另一个配置文件的所有特征(包括规则、插件和语言选项)并修改所有选项。
它的值可以是一个指定配置的字符串或一个字符串数组。
可共享配置包
即发布到 npm 上的包,导出的是一个配置对象。当你在项目根目录下安装了这个包后,ESLint 就可以使用它了。
一个共享配置包会以 eslint-config-
作为前缀,我们在 extends
中使用时可以直接省略前缀。请看以下示例:
{
"devDependencies": {
"eslint-config-airbnb": "^19.0.4",
"@vue/eslint-config-prettier": "^7.0.0",
"@vue/eslint-config-typescript": "^11.0.2",
}
}
安装了3个共享配置包,在使用时都直接省略前缀。
{
extends: [
'airbnb',
'airbnb/hooks',
"@vue/typescript",
"@vue/typescript/recommended",
"@vue/prettier",
]
}
需要注意的是,当 extends 是一个字符串数组时,每个额外的配置都会扩展前面的配置,也就是后面的 extends 会覆盖前面的 extends。
ESLint 内置的核心规则
eslint:recommended
:启用报告常见问题的核心规则子集;
eslint:all
:启用当前安装的 ESLint 版本中的所有核心规则。核心规则的集合会因 ESLint 的任何次要或主要版本改变而改变,因此不建议用于生产。
{
extends: [
"eslint:recommended",
"@vue/typescript/recommended",
"@vue/prettier",
],
}
最后,还有一种配置就是插件内的配置,详情参见 plugins 插件 章节。
plugins 插件
ESLint 插件是一个包含 ESLint 规则、配置、解析器和环境变量的集合的 npm 模块。通过插件包括自定义规则。插件可以强制使用某个风格指南并支持 JavaScript 扩展(比如 TypeScript)、库(比如 React)和框架(比如 Angular)。
插件的流行用例就是强制执行框架规定的最佳实践。比如 @angular-eslint/eslint-plugin 包括了使用 Angular 框架的最佳实践。
可以理解为插件就是别人写好并上传到 npm 上的一套 ESLint 包,里面定义了额外的规则、环境、配置、编译器等,开发者可以自行安装使用这些最佳实践。
插件的命名和使用规范
首先,插件名必须要含有 eslint-plugin-
前缀,最后才是插件的名字,比如 eslint-plugin-vue
。
像这种直接标明名字的包叫非范围包,还有一种 npm 包叫范围包,这种包会多出一个@org-name/
的前缀,比如 @typescript-eslint/eslint-plugin
,范围名称就是 @
和 斜杠/
之间的 typescript-eslint
,这种包一般是组织发布使用。对发包感兴趣的,可以参阅创建发布范围公共包。
无论是哪种包,在进行文件配置时,都可以省略 eslint-plugin-
前缀。事实上,我们也必须按照惯例来引用它们。以下是三种包的名称使用规范:
-
eslint-plugin-vue 简写 → vue
-
@typescript-eslint/eslint-plugin 简写 → @typescript-eslint
-
@tidio/eslint-plugin-rules 简写 → @tidio/rules
注册插件
在使用插件之前,你必须先使用 npm 安装它。然后使用 plugins
注册插件,它是由插件名称组成的数组列表:
{
plugins: ['vue', '@typescript-eslint', '@tidio/rules'],
}
接下来,我们就可以使用插件里定义的规则、环境或配置了。
使用插件配置
使用插件配置时,它的 extends
属性值由以下内容组成:
-
plugin:(区分 config 和 eslint:)
-
缩写报名
-
斜杠 /
-
插件中的配置名称(如 recommended)
请看以下示例:
{
plugins: ['react', 'unicorn', 'promise'],
extends: [
'plugin:react/recommended',
'plugin:unicorn/recommended',
'plugin:promise/recommended',
]
}
使用插件环境
使用插件中的环境前,一定要在 plugins
数组中指定插件的名称,然后使用 插件简称
+ /
+ 环境名称
,比如说:
{
"plugins": ["example"],
"env": {
"example/custom": true
}
}
修改插件规则
同理,要配置定义在插件中的规则,也必须在这条规则前加上 插件简称
和 /
。
{
plugins: ['react', 'unicorn', 'promise'],
rules: {
'unicorn/better-regex': 'error',
'react/jsx-indent': ['error', 4],
'promise/always-return': 'off',
}
}
parser 和 parserOptions
和 babel 一样,eslint 也是基于 AST 的。只是一个做代码的转换,一个做错误检查和修复。babel 插件和 eslint 插件都能够分析和转换代码。
默认情况下,ESLint 使用 Espree 作为其解析器(parser: '@/espree'
),而自定义解析器则可以让 ESLint 解析非标准的 JavaScript 语法。
parserOptions 则是用来设置解析器选项,并将直接传递给解析器的 parser() 方法。可选项有:
选项 | 说明 | 值 |
---|---|---|
ecmaVersion | 指定要使用的 ECMAScript 语法的版本 | 年份 或 "latest" |
sourceType | 文件资源类型 | "script" 或 "module"(ECMA 模块) |
allowReserved | 允许使用保留字作为标识符 | boolean |
ecmaFeatures | 表示想使用哪些额外的语言特性 | 具体如下 |
ecmaFeatures.globalReturn | 允许全局范围内的 return 语句 | boolean |
ecmaFeatures.impliedStrict | 全局严格模式 | boolean |
ecmaFeatures.jsx | 启用 JSX | boolean |
可用选项是基于解析器的,除上述公共的选项外,有些解析器还支持自定义的选项,比如 @typescript-eslint/parser
还支持 jsxPragma 选项,vue-eslint-parser
还支持parser选项。所以我们在使用时,应该先翻阅对应的文档。
TypeScript 解析器
如果你的项目使用了 TypeScript,可以安装并使用 @typescript-eslint/parser
来替代默认的 Espree
,它是一个将 TypeScript 转换为与 ESTree 兼容的形式的解析器,因此可以在 ESLint 中使用。
npm i @typescript-eslint/parser -D
{
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 11,
sourceType: 'module',
jsxPragma: 'React'
},
}
Vue 解析器
如果你的项目使用了 Vue + TypeScript,可以安装并使用 vue-eslint-parser
,它的 parserOptions
属性与默认的 Espree
所支持的属性相同。
npm i vue-eslint-parser @typescript-eslint/parser -D
{
parser: 'vue-eslint-parser',
parserOptions: {
sourceType: 'module',
ecmaVersion: 2018,
ecmaFeatures: {
globalReturn: false,
impliedStrict: false,
jsx: false
}
}
}
你也可以使用 parserOptions.parser
指定一个自定义的解析器来解析 <script> 标记:
{
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
sourceType: 'module',
ecmaVersion: 2020,
ecmaFeatures: {
globalReturn: false,
impliedStrict: false,
jsx: true
}
}
}
你还可以为<script lang="...">指定一个对象并单独更改解析器:
{
parser: 'vue-eslint-parser',
parserOptions: {
parser: {
// Script parser for `<script>`
js: 'espree',
// Script parser for `<script lang="ts">`
ts: '@typescript-eslint/parser',
// Script parser for vue directives (e.g. `v-if=` or `:attribute=`)
// and vue interpolations (e.g. `{{variable}}`).
// If not specified, the parser determined by `<script lang ="...">` is used.
"<template>": 'espree'
},
}
}
更多配置参见vue-eslint-parser
overrides
通过 overrides
配置项覆盖配置中基于文件 glob 模式的配置,让我们能够更加精细地对某些文件进行检查。举个例子大家就很好明白了:
{
overrides: [
{
files: ["*.ts", "*.vue"],
rules: {
"no-undef": "off"
}
},
{
files: ["*.vue"],
parser: "vue-eslint-parser",
parserOptions: {
parser: "@typescript-eslint/parser",
extraFileExtensions: [".vue"],
ecmaVersion: "latest",
ecmaFeatures: {
jsx: true
}
},
rules: {
"no-undef": "off"
}
}
]
}
上述配置中,我们针对所有的 .ts
和 .vue
文件,关闭了 "no-undef"
检查。其次,针对 .vue
文件,我们还重新指定了解析器的配置。
总结
本期围绕 ESLint 的一些配置项,着重介绍了他们的含义以及如何使用,让我们更好的了解 ESLint 的面貌,至少在编辑器报红时,不至于摸不着头脑。
下篇我们将在项目搭建中,围绕以下几点展开讨论:
- 如何使用工具或命令行直接快速生成 ESLint 配置;
- 如何通过命令自动检查、定位和修复代码;
- 更友好的 VSCode 配置;
- ESLint 与 Prettier 的冲突以及如何解决。
敬请期待哈~💋