从零开始 eslint

概述

eslint 是用来在 js 文件中识别和报告模式匹配的工具,它的目的是保证代码的一致性和避免错误。我们可以在编码的过程中配合相关的代码编辑工具提前发现错误或者不规范的代码,例如我们可以在 vscode 中直接看到不符合规则代码段;也可以配合相关的命令行工具执行代码检查并且发现错误,例如在 webpack 执行代码构建的过程中发现不符合规范的错误并且报告错误。

使用

eslint 的使用很方便,如果我们要在编码的过程中发现提前发现错误,我们需要配合相关的代码编辑器使用。下面以 vscode 为例做一个使用说明。

  1. 在 vscode 扩展程序中安装 eslint 扩展,并且启用扩展
  2. 在项目根路径下(这里涉及到 eslint 配置文件的优先级问题,后面会做说明) 编写 eslint 配置文件

只需要上面简单两步就可以达到在编辑器中提示错误的目的。但是如果你需要使用命令行工具检查错误,你需要本地或者全局安装 eslint,然后通过 eslint 提供的 cli 相关命令达到代码检查的目的。在我们的项目中通常需要两种方式同时使用,这样我们就需要在上面两步的基础之上在项目中安装 eslint(或者全局安装)。下面做一个完整的实例说明:

  1. 本地安装

    # 项目根目录下执行以下命令 project
    
    npm install eslint -D
    
  2. 编写 eslint 配置文件

    // project/.eslintrc.js
    
    module.exports = {
      rules: {
        'no-console': 'error'
      }
    }
    
  3. 在编辑器中编写代码如下

    // project/src/index.js
    
    console.log(123)
    

    然后我们可以看到如下错误

    错误

  4. 在命令行错误提示,执行如下命令

    # 本地安装
    npx eslint src/index.js
    
    # 如果是全局安装可以直接执行
    eslint src/index.js
    

    我们可以在命令行中看到如下错误

    错误

    上面几个简单的操作我们就可以实现 eslint 的错误提示功能

除了上面的使用方式,还可以根据需要以 nodejs API 的方式使用 eslint

配置说明

1. 支持的配置方式

在项目中可以使用两种方式来对 eslint 进行配置

  • 通过 js 注释的方式进行配置,例如:

    // eslint-disable-next-line no-console
    console.log(123)
    
  • 通过配置文件的方式进行配置,配置文件支持的多种格式的文件,如果存在多个只会采用一个,优先级顺序如下:

    • .eslintrc.js
    • .eslintrc.yaml
    • .eslintrc.yml
    • .eslintrc.json
    • package.json

如果存在多个配置文件,将会按照上面的顺序决定配置项,只会按照顺序选择优先级最高的配置文件。除此之外,还有层叠规则,层叠的意思也就是说多个配置文件会进行规则合并,优先级高的会覆盖优先级低的规则。下面看几个例子来说明优先级规则和层叠规则。

project
|—— src
|   |—— .eslintrc.yaml
|   |—— index.js
|—— .eslintrc.js
|—— .eslintrc.json

上面这种目录结果中 eslint 规则作用于 index.js 文件的规则可以描述为:

  • 根据优先级规则,project/.eslintrc.json 文件中的 eslint 规则不会起任何作用
  • 根据层叠规则,project/src/.eslintrc.yaml 会与 project/.eslintrc.js 规则合并,并且 project/src/.eslintrc.yaml 中的同名规则会覆盖 project/.eslintrc.js

如果不希望外层的规则生效,可以在内层的配置文件中添加 root 属性为 true。

2. 支持的配置项

兼容到 eslint 的灵活性,可以通过配置文件的方式对 eslint 每一条规则进行配置。一般来说一个 eslint 配置文件具有以下基本配置。

2.1 parserOptions

该配置项允许你设置你想要支持的 js 语言选项。考虑到 es 存在多个版本,且语法不尽相同,所以你可以在这个配置项中指定所需要支持的 es 版本。一个完整的配置可以参考下面:

module.exports = {
  "parserOptions": {
    // 需要支持的 es 版本,选择不同的版本将可以支持不同版本的语法特性,例如如果指定 ecmaVersion = 3,则无法支持 es6 中新的语法特性,像 const、let 都无法支持
    "ecmaVersion": 2015,

    // 表示你想支持额外的语言特性
    "ecmaFeatures": {
      // js 文件中允许使用 jsx
      "jsx": true,

      // js 文件中允许使用全局 return
      "globalReturn": true,

      // 全局严格模式
      "impliedStrict": true
    },
    "sourceType": "module"
  }
}

下面通过一个例子对 ecmaVersion 做个说明,对于下面这段代码不同的 ecmaVersion 会有不同的检查结果

const {x, y, ...rest} = {x: 1, y: 2, a: 3, b: 4}

如果配置文件中设置的 ecmaVersion 在 2018 以下,将会报错

error Parsing error: Unexpected token ..

如果 ecmaVersion 不低于 2018 将会 lint 成功

2.2 parser

通过 parser 可以指定一个代码解析器,eslint 默认使用的代码解析器是 esprima,解析器的作用是将符合 ecma 规范的代码转换成抽象语法树。常见的代码解析器除了 esprima 之外还有 babel 所使用的 babylon 以及 webpack 核心所使用的 acron,但是在 eslint 中指定的解析器需要满足特定的条件,所以无法直接使用 babylon 和 acron,目前我们在 eslint 中常用的代码解析器有 @babel/eslint-parse 和 @typescript-eslint/parser。配置示例如下:

module.exports = {
  "parser": "@typescript-eslint/parser",
  "rules": {
    "semi": "error"
  }
}

通常我们在对 ts 文件进行代码检查的时候需要用到 @typescript-eslint/parser,否则会出现 esprima 无法解析语法而导致报错。

2.3 processor

当我们需要为一些非 js 文件类型执行eslint 规则的检查时,需要为这些文件类型指定一个解析器,解析器可以用来将非 js 类型的文件转换成为可以被 eslint parser 所识别的语法,但是 processor 不能作为独立的配置项在使用,必须放在 plugins 中。例如想要对 json 文件像 js 文件一样使用 eslint 规则检查,需要使用一个 plugin,plugin 中需要导出固定的内容。看一下 eslint-plugin-json-processor 的实现。

module.exports = {
  processors: {
    '.json': {
      preprocess: function(text, filename) {
        return [
            { text: `(${ text })`, filename: filename },
        ]
      },

      supportsAutofix: false
    }
  }
}

eslint 在执行的过程中会根据 processors 的 key 进行匹配,如果跟所要检查的文件后缀相同,则会执行对应的解析器。

如果需要使用 eslint-plugin-json-processor 需要按照如下的方式:

module.exports = {
  plugins: [
    'eslint-plugin-json-processor'
  ],
  rules: {
    'quotes': ['error', 'single']
  }
}

这样就会对 json 文件中的引号起到检查作用。

2.4 globals

如果开启了 no-undef 规则,在文件中使用未声明的变量将会出现检查错误,此时我们可以在 globals 配置项中允许需要被使用的全局变量,lint 规则就不会发生检查错误。

// 如果我们在文件中使用了 window
window.appName = 'myApp'

此时 eslint 会报出如下错误

error 'window' is not defined no-undef

如果在这种场景下需要绕过 eslint 检查,可以在 globals 配置中允许使用 window

// .eslintrc.js
module.exports = {
  globals: {
    window: true
  }
}

这样就可以绕过对 window 的检查

2.5 env

有时候在文件中可能需要针对某一类的全局变量绕过检查,例如我们编写的代码时运行在浏览器环境中,常常会使用 windowdocument 这一类的全局变量,除了可以一个一个在 globals 中进行允许之外,还可以在 env 配置中设置某一个或某几个环境变量。下面列出一些常用的的环境类型,更多完整的可以在这里查看

env 说明
browser 允许使用浏览器环境中的全局变量,如:window、document
node 允许使用node环境中的全局变量,如:global、module
jquery 允许使用jquery全局变量,如:$、jQuery

2.6 rules

通过 rules 可以灵活地控制 eslint 的检查内容,在完成 eslint 安装之后,eslint 已经提供了很多内置的规则,可以根据实际项目的需要自定义是否选用这些规则。规则的设置有三个级别:

  • error or 2: 开启指定规则,检查会抛出错误,并且会打断检查进程的继续执行
  • warn or 1: 开启指定规则,检查会抛出警告,不会打断检查进程的继续执行
  • off or 0: 关闭指定规则

对于规则除了可以设置错误级别之外,还能通过一些额外配置对规则进行自定义的控制

例如 eslint 内置对引号检查的规则可以设置不同的值

// .eslintrc.js
module.exports = {
  rules: {
    // quotes: ['error', 'signle', {avoidEscape: true}]
    quotes: ['error', 'signle']
  }
}

从上面的实例中可以看到 quotes 这个规则可以除了设置错误级别之外,还有两个配置项,第一个配置项是一个 string,可选值如下:

  • single: 单引号 'single'
  • double: 双引号 "double"
  • backtick: 反引号 `backtick`

第二个配置项是一个对象,一共有两个属性

  • avoidEscape:布尔值,true 表示如果存在字符串嵌套的情况下最外层的引号允许使用单引号或者双引号,默认值为 false
  • allowTemplateLiterals:布尔值,true 表示允许使用反引号

在这里你可以查找到所有的 eslint 内置规则

2.7 plugins

插件可以为 eslint 添加其原本不具有的能力,例如想要在 eslint 内置规则之上添加新的规则,还例如需要为非 js 文件转换成可以被 eslint 所解析的语法格式,像 vue 文件是无法被 eslint 的解析器所识别的,为了可以对 vue 文件进行检查我们可以安装 eslint-plugin-vue 插件。

在不使用 eslint-plugin-vue 之前对 vue 文件进行lint,会出现如下错误:

error Parsing error: Unexpected token <

这是因为在分词阶段就已经出错了,无法执行后续解析,如果我们执行如下配置之后

// .eslintrc.js
module.exports = {
  extends: [
    'plugin:vue/base'
  ]
}

则会出现如下的一些错误或者警告

warning Expected 1 space after '{{', but not found vue/mustache-interpolation-spacing

这说明规则已经生效了,上面的示例中并没有具体地为 eslint 指定 parser 和 需要的规则,但是却可以对 vue 文件进行检查。这是因为在 extends plugin 之后同样会继承插件中指定的 parser 和 rules,可以这样理解每一个插件都具备一个完整的 eslint 配置项,插件里面可以指定规则、解析器、变量环境、处理器、不受 no-undef 约束的全局变量。可以看到 eslint-plugin-vue 已经在插件中指定好了 parser 为 vue-eslint-parser,并且提供了一系列的的规则。同时需要注意,在上面的示例中除了可以 extends plugins:vue/base 之外还可以 extends plugin:vue/recommended,这两个不同的配置提供的规则是不一样的。实际上为了在同一个插件中指定不同的检查规则,可以为插件提供一个 config 配置。下面这个例子中同一个插件就导出了两个不同的配置项。

// eslint-plugin-myPlugin

module.exports = {
  configs: {
    myConfig: {
      plugins: ["myPlugin"],
      env: ["browser"],
      rules: {
        semi: "error",
        "myPlugin/my-rule": "error",
        "eslint-plugin-myPlugin/another-rule": "error"
      }
    },
    myOtherConfig: {
      plugins: ["myPlugin"],
      env: ["node"],
      rules: {
        "myPlugin/my-rule": "off",
        "eslint-plugin-myPlugin/another-rule": "off"
        "eslint-plugin-myPlugin/yet-another-rule": "error"
      }
    }
  }
};

2.8 extends

上面关于 plugins 的内容中已经涉及到了 extends,extends 主要是用来实现规则继承的。

extends 属性值可以是:

  • 指定配置的字符串(配置文件的路径、可共享配置的名称、eslint:recommended 或 eslint:all)
  • 字符串数组:每个配置继承它前面的配置,如果出现同样的规则,后面的会覆盖前面的

通常可以在 npm 仓库中找到其他人整理好的规则集合,例如 eslint-cofnig-airbnb,如果需要使用可以使用规则继承

module.exports = {
  extends: ['eslint-config-airbnb']
}
posted @ 2020-12-13 16:37  金神敏  阅读(674)  评论(0)    收藏  举报