ESLint入门与实践初步

ESLint 入门与实践


一、主流JavaScript检查调研

JSLint、JSHint、JSCS、ESLint 四种工具有相同的基本方式工作。它们都有一套用户分析、报告js文件错误的规则。他们都可以通过npm安装、通过命令行使用。下面比对一下各自的优缺点

JSLint

JSLint 是其中最老的工具。在2002年 Douglas Crockford,业内尊称“道爷”开发了该工具,根据其经验,强制使用js语言中精粹的部分。如果你同意这些精粹,JSLint能成为一个好的工具。

优点

  • 参数配置完成,可以直接使用

缺点

  • JSLint 不存在配置文件,如果想改变参数设置,那就存在问题
  • 有限的配置选项,许多规则不能禁掉
  • 不能增加个性化规则
  • 很难弄清楚哪个规则引起的错误

JSHint

作为一个可配置的JSLint版本而被开发出来

优点

  • 大多是参数可以配置
  • 支持配置文件,在大项目中容易使用
  • 已经支持需要类库,像jQuery、QUnit、NodeJS、Mocha等
  • 支持基本的ES6

缺点

  • 难于知道哪个规则产生错误
  • 存在两类选项:强制选项和松散选项。使得配置有些混乱
  • 不支持自定义规则

JSCS

JSCS是一个代码风格检查器。这意味着它仅仅匹配代码格式的问题,不匹配潜在的bugs、errors。因此,跟其他工具相比缺少灵活性,但是如果你仅仅强制检查代码风格,JSCS也是一个好的工具。

优点

  • 支持自定义报告,更容易与其他工具集成
  • 如果你遵循一种可用的代码风格,配置项和准备好的配置文件使其容易启动
  • 在报告中存在标记包含规则名字,所以很容易指出哪个规则造成了错误
  • 通过自定义插件进行拓展

缺点

  • 仅仅检查代码风格的问题。JSCS不检查潜在存在的bugs,例如不适用的变量、偶然的全局变量等等
  • 四个工具中最慢,但是在使用中不是一个问题

ESLint

ESLint是最新出来的工具,是一个用来识别 ECMAScript 并且按照规则给出报告的代码检测工具,使用它可以避免低级错误和统一代码的风格。它被设计的容易拓展、拥有大量的自定义规则、容易的通过插件来安装。它给出准确的输出,而且包括规则名,这样可以知道哪个规则造成了错误。

优点

  • 灵活:任何规则都可以开启闭合,以及有些规则有些额外配置
  • 很容易拓展和有需要可用插件
  • 容易理解产出
  • 包含了在其他检查器中不可用的规则,使得ESLint在错误检查上更有用
  • 支持ES6,唯一支持JSX的工具
  • 支持自定义报告

缺点

  • 需要一些配置
  • 速度慢,但不是主要问题

总结

最终推荐 ESLint。JSLint是严格和不可配置的,而JSHint缺少拓展机制。JSCS如果仅仅用于代码风格检验是一个好的选择,但是ESLint不仅可以进行代码风格的检验,而且可以检查代码中的bug和其他问题。
如果使用ES6,ESLint也是明显的选择。在上面提到的工具中,ESLint对ES6支持的最广泛。

二、ESLint 安装

安装

npm i -g eslint
npm install eslint --save-dev

初始化配置文件

接下来新建一个配置文件.eslintrc.js,或者执行eslint --init来自动生成。这条命令会类似npm init出现一系列对话问答

eslint_init.png-178.4kB

通过结果产生基本的配置内容,内容如下(不包含注释,注释是我写的)

module.exports = {
    "env": {
        "browser": true
    },
    "extends": "eslint:recommended",
    "rules": {
        // 强制使用一致的缩进: error级别,四空格
        "indent": [
            "error",
            4
        ],
        // 强制使用unix风格的换行:error级别
        "linebreak-style": [
            "error",
            "unix"
        ],
        // 使用双引号“”,而不是‘’
        "quotes": [
            "error",
            "double"
        ],
        // 强制行位分号
        "semi": [
            "error",
            "always"
        ]
    }
};

还可以选择在package.json 中设置 eslintConfig属性。但是不推荐。

个人建议, 使用单独的 .eslintrc.* 配置文件,这样别人一看代码结构就知道使用了 eslint 来校验代码,最好是js文件,json文件是不支持注释的。

默认情况下,eslint 会在所有父级文件夹中寻找配置文件,一直找到根目录为止。如果希望 eslint 不要继续往外寻找配置文件了则这样配置:"root": true

.eslintignore 忽略文件

你可以通过在项目根目录创建一个 .eslintignore 文件告诉 ESLint 去忽略特定的文件和目录。

  • # 开头的行被当作注释,不影响忽略模式。
  • 路径是相对于 .eslintignore 的位置或当前工作目录。这也会影响通过–ignore-pattern传递的路径。
  • 忽略模式同 .gitignore 规范
  • 除了 .eslintignore 文件中的模式,ESLint总是默认忽略 /node_modules//bower_components/中的文件。

三、ESLint 配置

配置执行环境

配置文件中可以自由的指定执行环境,浏览器 或 nodejs。可以同时指定多个!

module.exports = {
  env: {
    browser: true,
    node: true,
    es6: true
  },
};

配置全局变量

只配置环境是远远不够的,不同环境之间还会有不同环境变量。ESLint 可以在配置文件或注释中指定额外的全局变量,false表明变量只读

// .eslintrc.js
module.exports = {
  globals: {
    var1: true,
    var2: true,
  },
};

配置匹配规则

在配置文件中可以设置一些规则。这些规则的等级有三种:

  • “off” 或者 0: 关闭规则。
  • “warn” 或者 1: 打开规则,并且作为一个警告(不影响 exit code)。
  • “error” 或者 2: 打开规则,并且作为一个错误(exit code 将会是1)。

配置文件支持灵活的多种方式

  • .eslintrc.* 配置文件中统一配置

    // .eslintrc.js
    module.exports = {
      rules: {
        eqeqeq: 'off',
        curly: 'error',
      },
    };
    
  • 文件中的注释和跳过规则

    /* eslint-disable */
    // 中间的代码,会被跳过检查所有的代码
    /* eslint-enable */
    
    /* eslint-disable no-alert, no-console */
    // 中间的代码,会被跳过检查列举的而两个规则
    /* eslint-enable no-alert, no-console */
    
    // eslint-disable-next-line
    console.log('test')
    

继承规则

使用配置文件设置规则是,既可以选择只在rules中设置,还可以选择从他处继承。我们可以将定义好规则的.eslintrc.js文件存储到一个公共的位置,比如public-eslintrc.js

module.exports = {
  extends: 'eslint:recommended',
  env: {
    node: true,
  },
  rules: {
    'no-console': 'off',
    'indent': [ 'error', 2 ],
    'quotes': [ 'error', 'single' ],
  },
};

然后将原来的.eslintrc.js文件改成这样:

module.exports = {
  extends: './public-eslintrc.js',
};

我们还可以使用已经发布到 NPM 上的 ESLint 配置,这些配置的模块名一般以eslint-config-为前缀,比如我在学习ESLint时自己编写的一个配置名为eslint-config-xxx。要使用这个配置,先执行以下命令安装它:

npm install -g eslint-config-xxx

用于我们的eslint命令是全局安装的,所有用到的eslint-config-*模块也必须全局安装,否则将无法正确载入。这是一个已知的Bug,参考这里:Error: Cannot read config package for shareable config using global eslint #4822

然后将.eslintrc.js文件改成这样:

module.exports = {
  extends: 'xxx',
};

四、执行

命令行执行

eslint [options] file.js [file.js] [dir]

这条命令中ESLint会自动找到当前目录下的.eslintrc.*文件

注意eslint merge.js --fix添加--fix结尾可以zi自动修复有橙色扳手图标的规则

配置到编译器

本文已intelliJ IDEA为例

eslint_idea.png-246.4kB
eslint_idea2.png-59.6kB

使用自动化任务

gulp-eslint

五、实践 && gulp-eslint

安装与介绍

gulp-eslint,是继承了 ESLint 功能的 gulp 插件,使用起来很简单,类似于命令行操作,直接安装

npm install gulp-eslint --save-dev

静态检查自动化任务

有了上面的基础,下面我们实现一个基础的自动化检查代码的gulp任务

  • 1.安装依赖

    {
      "name": "eslint_learning",
      "version": "0.0.0",
      "description": "eslint_learning",
      "private": false,
      "dependencies": {
        "eslint-plugin-html": "^3.2.2",
        "gulp": "3.9.1",
        "gulp-eslint": "1.0.0"
      }
    }
    
  • 2.编写 eslint 配置文件

    {
      "ecmaFeatures": {
        "modules": true
      },
      "plugins": [
          "html"
      ],
      "env": {
        "es6": true,
        "browser": true
      },
      "rules": {
        "no-console": 2,
      }
    }
    
  • 3.编写 gulpfile 配置文件,添加自动化任务

    var gulp = require('gulp'),
            eslint = require('gulp-eslint');
    
        gulp.task('lint', function() {
            return gulp.src(['js/**/*.js', 'index.html'])
                //.pipe(eslint({configFle:"./.eslintrc"}))
                .pipe(eslint())
                .pipe(eslint.format())
        });
    
        gulp.task('default', ['lint'], function() {
            // This will only run if the lint task is successful...
    });
    
  • 4.测试内容:index.html 中的javascript

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>eslint demo</title>
    </head>
    <body>
        <div>hello world</div>    
        <script src="js/index.js"></script>
        <script type="text/javascript">
            var a = 1;
        </script>
    </body>
    </html>
    
  • 5.测试内容 js/index.js

    let a = 1;
    //console.log('ddd')
    
  • 6.运行任务,查看结果

    gulp lint
    

六、git hooks

git 给我们提供了很多钩子函数,用于给不同的命令如,git commit指令提供类似回调函数,会在对应的指令的恰当时机执行。本文中模拟的是在代码编写完毕执行git commit命令之前的钩子pre-commit

githooks1.png-78kB

直接自定义 pre-commit

我们去掉文件末尾的.sample部分,使 git 能够识别。重写里面的内容

#!/bin/sh
#执行gulp任务,并将结果输出到临时文件
gulp lint | tee check.log

#检查gulp的check任务是否执行失败
if grep "warning" check.log || grep "error" check.log
then
echo -e "\033[31m Code check fail! Please try again! \033[0m"
rm check.log
exit 1
else
echo -e "\033[32m Code check success! \033[0m"
rm check.log
fi

然后安装插件 eslint-plugin-html

sudo npm install eslint-plugin-html --save-dev

接下来我们尝试一下带着问题提交,

git commit -m

果然,git 按照我们代码中写的那样,首先提示我们有错误,然后打断了提交流程

githooks2.png-136.5kB

使用 插件

第一步:然后安装插件 eslint-plugin-html

sudo npm install eslint-plugin-html --save-dev

安装完这个插件,会默认修改前文提到过的pre-commit.samplepre-commit

第二步:修改 package.json,添加pre-commit字段,设置git commit操作之前的任务

{
  "name": "eslint_learning",
  "version": "1.2.1",
  "description": "eslint_learning",
  "private": false,
  "scripts": {
    "lints": "num=`gulp lint | grep 'problem'|wc -l`;if [ $num -gt 0 ]; then echo wrongEslint;exit 2; else exit 0;fi",
    "precommit-msg": "echo 'Pre-commit checks...' && exit 0"
  },
  "dependencies": {
    "eslint-plugin-html": "^3.2.2",
    "gulp": "3.9.1",
    "gulp-eslint": "1.0.0"
  },
  "pre-commit": [
    "precommit-msg",
    "lints"
  ],
  "devDependencies": {
    "pre-commit": "^1.2.2"
  }
}

可见,我们设置了两个前置任务

第三部:命令保存退出。接下来我们尝试一下带着问题提交,

git commit -m

命令行提示如下内容

xxxdeMacBook-Pro:test1 xxx$ git commit -m "ddd"
Pre-commit checks...
wrongEslint
pre-commit:
pre-commit: We've failed to pass the specified git pre-commit hooks as the `lints`
pre-commit: hook returned an exit code (1). If you're feeling adventurous you can
pre-commit: skip the git pre-commit hooks by adding the following flags to your commit:
pre-commit:
pre-commit:   git commit -n (or --no-verify)
pre-commit:
pre-commit: This is ill-advised since the commit is broken.
pre-commit:

八、推荐文章

ESLint中文网