ESLint 非权威配置指北(上)

ESLint 这玩意儿用起来让人又爱又恨,一大堆配置能弄出一堆风格,稍不留神就整个红色波浪线恶心一下你,今天来好好瞧瞧这小可爱到底怎么配。

本文不讲解具体的规则内容,而是手把手告诉你 ESLint 有哪些配置项,都是什么意思,应该如何去配。建议点赞收藏,在开发项目时可以先看手册,再配合官方文档查看细节。

文件格式

常用的配置文件有 .js.json 两种。其中,.js 的优先级更高。在 ESLint JSON 文件中,你也可以放心使用 JS 风格的注释(// 注释……),ESLint 会安全地忽略配置文件中的注释。

个人比较喜欢 .js 风格,可以在里面定义变量或者做一些额外的处理。

除了上述两种风格外,还有 cjsyamlyml格式,甚至也可以直接在 package.json 中直接配置,有兴趣的可以参考配置文件

配置对象

一套完整的 ESLint 文件大概长这样:

module.exports = {
root: true,
env: {},
globals: {},
extends: {},
parser: 'xxxxxx'
parserOptions: {},
plugins: [],
rules: {},
// ......
}
module.exports = {





    root: true,

    env: {},
    globals: {},
    extends: {},
    parser: 'xxxxxx'
    parserOptions: {},
    plugins: [],
    rules: {},
    // ......
}
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,
};
module.exports = {





    root: true,

};
module.exports = { root: true, };

env 环境与变量

使用 env 指定环境,并通过将每个环境设置为 true 来启用想要的环境。

所以环境是什么?举个例子大家就一目了然了。

我们先在配置文件中添加一条规则:不允许使用未定义的变量。

module.exports = {
rules: {
'no-undef': 'error',
},
};
module.exports = {





    rules: {
        'no-undef': 'error',
    },

};
module.exports = { rules: { 'no-undef': 'error', }, };

此时会发现这条规则对子自个儿报错了,真的是大义灭亲啊……

Snipaste_2023-06-27_09-47-03.png

鼠标 hover 看下报错原因:

Snipaste_2023-06-27_09-50-05.png

很明显,在 ESLint 看来 module 并没有被明确定义,所以触发了这条规则。但作为 Node 环境中内置的全局变量,它确确实实存在,所以我们必须在 env 中设置 node: true 来显示地告诉 ESLint 当前的运行环境为 Node。

module.exports = {
env: {
node: true
}
rules: {
'no-undef': 'error',
},
};
module.exports = {





    env: {

      node: true
    }

    rules: {

        'no-undef': 'error',
    },

};
module.exports = { env: { node: true } rules: { 'no-undef': 'error', }, };

此时,ESLint 就会添加 Node.js 的全局变量,module 就不会报错了。于此相似的还有浏览器中的 window 变量。

比如,我们创建一个 test.js 文件,随便写点东西:

window.test = '123';
console.log(111);
window.test = '123';

console.log(111);
window.test = '123'; console.log(111);

会发现 ESLint 又报错了,原因也是变量未定义。

Snipaste_2023-06-27_10-11-29.png

同理,我们需要在 env 中指定 浏览器的全局变量:

module.exports = {
env: {
node: true,
browser: true
}
rules: {
'no-undef': 'error',
},
};
module.exports = {





    env: {

      node: true,
      browser: true
    }

    rules: {
        'no-undef': 'error',
    },

};
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"
},
}
module.exports = {





    globals: {
        wx: 'writable', // readonly-只读 / writable-可改
    },

    rules: {

        "no-undef": "error"
    },

}
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"]
}
}
{












    "rules": {

        "quotes": ["error", "double"]
    }

}
{ "rules": { "quotes": ["error", "double"] } }

quotes 数组的第一个元素表示严重程度为 错误,第二个元素表示 双引号,说明必须使用双引号,否则 ESLint 会报错。

有些规则会有多个配置项,这时可以根据文档的说明,继续往后添加自己需要的options。比如 operator-linebreak,它会为运算符强制执行一致的换行样式:

{
"rules": {
'operator-linebreak': [
'warn',
'after',
{
overrides: {
'?': 'before',
':': 'before',
},
},
],
}
}
{












    "rules": {

        'operator-linebreak': [
            'warn',
            'after',
            {
                overrides: {
                    '?': 'before',
                    ':': 'before',
                },
            },
        ],
    }
}
{ "rules": { 'operator-linebreak': [ 'warn', 'after', { overrides: { '?': 'before', ':': 'before', }, }, ], } }

参数一: 警告级别的严重程度;

参数二:要求在运算符之后换行;

参数三:是一个对象,其中的 overrides 可以覆盖全局的规则,针对个别运算符单独配置。

所以基于该换行规则的正确写法就成了这样:

foo = 1 + 2;
// 或者在运算符后面换行
foo = 1 +
2;
// 三元运算符则需要在 '?' 和 ':' 之前换行
answer = everything
? 42
: foo;
foo = 1 + 2;
// 或者在运算符后面换行
foo = 1 +
      2;

// 三元运算符则需要在 '?' 和 ':' 之前换行
answer = everything
  ? 42
  : foo;
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",
}
}
{
    "devDependencies": {
        "eslint-config-airbnb": "^19.0.4",
        "@vue/eslint-config-prettier": "^7.0.0",
        "@vue/eslint-config-typescript": "^11.0.2",
    }
}
{ "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: [

    'airbnb',
    'airbnb/hooks',
    "@vue/typescript",
    "@vue/typescript/recommended",
    "@vue/prettier",
  ]
}
{ 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",
],
}
{












  extends: [

    "eslint:recommended",
    "@vue/typescript/recommended",
    "@vue/prettier",
  ],
}
{ 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'],
}
{












  plugins: ['vue', '@typescript-eslint', '@tidio/rules'],
}
{ plugins: ['vue', '@typescript-eslint', '@tidio/rules'], }

接下来,我们就可以使用插件里定义的规则、环境或配置了。

使用插件配置

使用插件配置时,它的 extends 属性值由以下内容组成:

  1. plugin:(区分 config 和 eslint:)

  2. 缩写报名

  3. 斜杠 /

  4. 插件中的配置名称(如 recommended)

请看以下示例:

{
plugins: ['react', 'unicorn', 'promise'],
extends: [
'plugin:react/recommended',
'plugin:unicorn/recommended',
'plugin:promise/recommended',
]
}
{












  plugins: ['react', 'unicorn', 'promise'],

  extends: [
    'plugin:react/recommended',
    'plugin:unicorn/recommended',
    'plugin:promise/recommended',
  ]
}
{ plugins: ['react', 'unicorn', 'promise'], extends: [ 'plugin:react/recommended', 'plugin:unicorn/recommended', 'plugin:promise/recommended', ] }

使用插件环境

使用插件中的环境前,一定要在 plugins 数组中指定插件的名称,然后使用 插件简称 + / + 环境名称,比如说:

{
"plugins": ["example"],
"env": {
"example/custom": true
}
}
{












    "plugins": ["example"],
    "env": {
        "example/custom": true
    }

}
{ "plugins": ["example"], "env": { "example/custom": true } }

使用插件规则

同理,要配置定义在插件中的规则,也必须在这条规则前加上 插件简称/

{
plugins: ['react', 'unicorn', 'promise'],
rules: {
'unicorn/better-regex': 'error',
'react/jsx-indent': ['error', 4],
'promise/always-return': 'off',
}
}
{












  plugins: ['react', 'unicorn', 'promise'],

  rules: {
    'unicorn/better-regex': 'error',
    'react/jsx-indent': ['error', 4],
    'promise/always-return': 'off',
  }
}
{ 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
npm i @typescript-eslint/parser -D
npm i @typescript-eslint/parser -D
{
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 11,
sourceType: 'module',
jsxPragma: 'React'
},
}
{












  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    ecmaVersion: 11,
    sourceType: 'module',
    jsxPragma: 'React'
  },
}
{ 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
npm i vue-eslint-parser @typescript-eslint/parser -D
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
}
}
}
{












    parser: 'vue-eslint-parser',


    parserOptions: {


        sourceType: 'module',
        ecmaVersion: 2018,
        ecmaFeatures: {
            globalReturn: false,
            impliedStrict: false,
            jsx: false
        }
    }
}
{ 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
}
}
}
{












    parser: 'vue-eslint-parser',


    parserOptions: {


        parser: '@typescript-eslint/parser',
        sourceType: 'module',
        ecmaVersion: 2020,
        ecmaFeatures: {
            globalReturn: false,
            impliedStrict: false,
            jsx: true
        }
    }
}
{ 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'
},
}
}
{












    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'
        },
    }
}
{ 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"
}
}
]
}
{












    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"
      }
    }
  ]
}
{ 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 的面貌,至少在编辑器报红时,不至于摸不着头脑。

下篇我们将在项目搭建中,围绕以下几点展开讨论:

  1. 如何使用工具或命令行直接快速生成 ESLint 配置;
  2. 如何通过命令自动检查、定位和修复代码;
  3. 更友好的 VSCode 配置;
  4. ESLint 与 Prettier 的冲突以及如何解决。

敬请期待哈~?

参考资料

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYRnD9Sg' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片