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
出现一系列对话问答
通过结果产生基本的配置内容,内容如下(不包含注释,注释是我写的)
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为例
使用自动化任务
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
直接自定义 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 按照我们代码中写的那样,首先提示我们有错误,然后打断了提交流程
使用 插件
第一步:然后安装插件 eslint-plugin-html
sudo npm install eslint-plugin-html --save-dev
安装完这个插件,会默认修改前文提到过的pre-commit.sample
为pre-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: