0%

项目规范化-ESLint的使用

Lint工具用于检查代码的语法是否正确、风格是否符合要求。

JavaScript语言的最早的Lint工具,是Douglas Crockford开发的JSLint。由于该工具所有的语法规则,都是预设的,用户无法改变。所以,很快就有人抱怨,JSLint不是让你写成正确的JavaScript,而是让你像Douglas Crockford一样写JavaScript。

JSHint可以看作是JSLint的后继者,最大特定就是允许用户自定义自己的语法规则,写在项目根目录下面的.jshintrc文件。

JSLint和JSHint同时检查你的语法和风格。另一个工具JSCS则是只检查语法风格。

最新的工具ESLint不仅允许你自定义语法规则,还允许用户创造插件,改变默认的JavaScript语法,比如支持ES6和JSX的语法。

ESLint最初是由Nicholas C. Zakas 于2013年6月创建的开源项目。它的目标是提供一个插件化的javascript代码检测工具。

快速上手

安装npm模块:

1
npm install eslint --save-dev

在测试之前,我们首先编辑一个测试文件:

1
2
3
4
5
6
7
8
9
10
// main.js
function add(a, b) {
return a + b
}

const name = 'Jinx'

console.log(add(1, 3, 4)

run()

使用命令行测试:

1
npx eslint ./main.js

执行后出现以下提示:

1
2
3
4
5
6
7
8
9
10
11
ops! Something went wrong! :(

ESLint: 7.16.0

ESLint couldn't find a configuration file. To set up a configuration file for this project, please run:

eslint --init

ESLint looked for configuration files in D:\Test\eslint-test and its ancestors. If it found none, it then looked in your home directory.

If you think you already have a configuration file or if you need more help, please stop by the ESLint chat room: https://eslint.org/chat/help

这是说eslint未找打一个配置文件,可以使用eslint --init为你的项目初始化一个配置文件。

执行eslint --init之后会有很多选项可供选择:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 你希望如何使用eslint?
# 从这里可以看出eslint主要的三个工作:代码语法检测、查找问题代码以及强制执行代码样式
? How would you like to use ESLint? ...
To check syntax only
To check syntax and find problems
> To check syntax, find problems, and enforce code style

# 你的项目是什么模块类型?
# 这里我们暂时什么都没使用,选择None即可
? What type of modules does your project use? ...
JavaScript modules (import/export)
CommonJS (require/exports)
> None of these

# 你的项目使用了哪个框架?
? Which framework does your project use? ...
React
Vue.js
> None of these

# 你的项目是否使用了ts?
? Does your project use TypeScript? » No / Yes

# 你的代码将在哪种环境运行?
# 浏览器或者Node环境
? Where does your code run? ... (Press <space> to select, <a> to toggle all, <i> to invert selection)
√ Browser
Node

# 你想如何定义你的项目代码风格?
# 你可以使用自定义的风格,也可以使用目前主流的一些代码风格(推荐)
? How would you like to define a style for your project? ...
> Use a popular style guide
Answer questions about your style
Inspect your JavaScript file(s)

# 你想遵循以下哪种分代码风格?
# 根据个人或团队偏好来选择即可
? Which style guide do you want to follow? ...
Airbnb: https://github.com/airbnb/javascript
> Standard: https://github.com/standard/standard
Google: https://github.com/google/eslint-config-google

# 配置文件的格式?
? What format do you want your config file to be in? ...
> JavaScript
YAML
JSON

# 选择完成之后会进行配置文件的创建,以及安装一些依赖,选 yes 即可。

配置文件初始化结束,就可以在项目根目录看到eslintrc.js这样的配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module.exports = {
env: {
browser: true,
es2021: true
},
extends: [
'standard'
],
parserOptions: {
ecmaVersion: 12
},
rules: {
}
}

我们暂时不用管这个配置文件,再次使用eslint来检测代码:

1
npx eslint ./main.js

然后我们发现命令行打印了这样的错误:

1
2
3
4
D:\Test\eslint-test\main.js
9:1 error Parsing error: Unexpected token run

✖ 1 problem (1 error, 0 warnings)

这是语法错误,因为consologe.log缺少)导致,我们手动补充上,再次执行检测,发现错误变成了6个:

1
2
3
4
5
6
7
8
9
10
D:\Test\eslint-test\main.js
1:13 error Missing space before function parentheses space-before-function-paren
2:1 error Expected indentation of 2 spaces but found 4 indent
5:7 error 'name' is assigned a value but never used no-unused-vars
7:1 error Expected indentation of 0 spaces but found 4 indent
9:1 error 'run' is not defined no-undef
9:6 error Newline required at end of file but not found eol-last

✖ 6 problems (6 errors, 0 warnings)
4 errors and 0 warnings potentially fixable with the `--fix` option.

在语法错误的情况下,这些错误没有检测出来,语法错误修正后,剩下的错误就是问题代码和代码风格错误。

对于代码风格问题,我们可以使用--fix的扩展命令进行修复,修复之后,还有两个报错:

1
2
3
4
5
D:\Test\eslint-test\main.js
5:7 error 'name' is assigned a value but never used no-unused-vars
9:1 error 'run' is not defined no-undef

✖ 2 problems (2 errors, 0 warnings)

而身下的这两个就是问题代码提示了,no-unused-vars是提示我们name这个变量定义了却未使用,而no-undef是提示run这个变量未定义。

eslint主要进行三方面的检测:

  • 语法检测。会阻断其他类型的检测。需要手动修复
  • 问题代码检测。需要手动修复。
  • 代码风格检测。使用--fix命令进行自动修正。

关于命令行的更多使用请参考:命令行选项

配置文件

虽然我们使用eslint --init进行了配置文件初始化,但有时候我们有一些自定义的需求,就需要重新修改配置文件。

环境 - env

一个环境定义了一组预定义的全局变量。可用的环境包括:

  • browser - 浏览器环境中的全局变量。
  • node - Node.js 全局变量和 Node.js 作用域。
  • commonjs - CommonJS 全局变量和 CommonJS 作用域 (用于 Browserify/WebPack 打包的只在浏览器中运行的代码)。
  • shared-node-browser - Node.js 和 Browser 通用全局变量。
  • es6 - 启用除了 modules 以外的所有 ECMAScript 6 特性(该选项会自动设置 ecmaVersion 解析器选项为 6)。
  • worker - Web Workers 全局变量。
  • amd - 将 require()define() 定义为像 amd 一样的全局变量。
  • mocha - 添加所有的 Mocha 测试全局变量。
  • jasmine - 添加所有的 Jasmine 版本 1.3 和 2.0 的测试全局变量。
  • jest - Jest 全局变量。
  • phantomjs - PhantomJS 全局变量。
  • protractor - Protractor 全局变量。
  • qunit - QUnit 全局变量。
  • jquery - jQuery 全局变量。
  • prototypejs - Prototype.js 全局变量。
  • shelljs - ShellJS 全局变量。
  • meteor - Meteor 全局变量。
  • mongo - MongoDB 全局变量。
  • applescript - AppleScript 全局变量。
  • nashorn - Java 8 Nashorn 全局变量。
  • serviceworker - Service Worker 全局变量。
  • atomtest - Atom 测试全局变量。
  • embertest - Ember 测试全局变量。
  • webextensions - WebExtensions 全局变量。
  • greasemonkey - GreaseMonkey 全局变量。

这些环境并不是互斥的,所以你可以同时定义多个。

可以在源文件里、在配置文件中或使用 命令行--env 选项来指定环境。

1
2
3
4
5
6
{
"env": {
"browser": true,
"node": true
}
}

全局变量globals

要在配置文件中配置全局变量,请将 globals 配置属性设置为一个对象,该对象包含以你希望使用的每个全局变量。

1
2
3
4
5
6
{
"globals": {
"jquery": "writable",
"md5": "readonly"
}
}
  • writable:允许重写变量。
  • readonly:只读变量。

解析器选项 - parserOptions

ESLint 允许你指定你想要支持的 JavaScript 语言选项。默认情况下,ESLint 支持 ECMAScript 5 语法。你可以覆盖该设置,以启用对 ECMAScript 其它版本和 JSX 的支持。

注意,支持 ES6 语法并不意味着同时支持新的 ES6 全局变量或类型(比如 Set 等新类型)。对于 ES6 语法,使用 { "parserOptions": { "ecmaVersion": 6 } };对于新的 ES6 全局变量,使用 { "env":{ "es6": true } }. { "env": { "es6": true } } 自动启用es6语法,但 { "parserOptions": { "ecmaVersion": 6 } } 不自动启用es6全局变量。

解析器选项可以在 .eslintrc.* 文件使用 parserOptions 属性设置。可用的选项有:

  • ecmaVersion - 默认设置为 3,5(默认), 你可以使用 6、7、8、9 或 10 来指定你想要使用的 ECMAScript 版本。你也可以用使用年份命名的版本号指定为 2015(同 6),2016(同 7),或 2017(同 8)或 2018(同 9)或 2019 (same as 10)
  • sourceType - 设置为 "script" (默认) 或 "module"(如果你的代码是 ECMAScript 模块)。
  • ecmaFeatures - 这是个对象,表示你想使用的额外的语言特性:
    • globalReturn - 允许在全局作用域下使用 return 语句
    • impliedStrict - 启用全局 strict mode (如果 ecmaVersion 是 5 或更高)
    • jsx - 启用 JSX
    • experimentalObjectRestSpread - 启用实验性的 object rest/spread properties 支持。(重要:这是一个实验性的功能,在未来可能会有明显改变。 建议你写的规则 不要 依赖该功能,除非当它发生改变时你愿意承担维护成本。)
1
2
3
4
5
6
7
8
9
{
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
}

扩展配置extends

一个配置文件可以被基础配置中的已启用的规则继承。

extends 属性值可以是:

  • 指定配置的字符串(配置文件的路径、可共享配置的名称、eslint:recommendedeslint:all)
  • 字符串数组:每个配置继承它前面的配置

检测规则rules

ESLint 附带有大量的规则。你可以使用注释或配置文件修改你项目中要使用的规则。要改变一个规则设置,你必须将规则 ID 设置为下列值之一:

  • "off"0 - 关闭规则
  • "warn"1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出)
  • "error"2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)

规则配置比较多,可参考别的文件是怎么配置的。

插件 -plugins

完全插件化正是eslint的设计初衷,所以插件的使用也是很简单的。在配置文件里配置插件时,可以使用 plugins 关键字来存放插件名字的列表。插件名称可以省略 eslint-plugin- 前缀。

1
2
3
4
5
6
{
"plugins": [
"plugin1",
"eslint-plugin-plugin2"
]
}

配置注释Configuration Comments

除了使用配置文件,eslint还支持使用配置注释的方式把规则来嵌入源码中。

为了在文件注释里配置规则,使用以下格式的注释,在下面这个例子里,eqeqeq 规则被关闭,curly 规则被打开,定义为错误级别

1
/* eslint eqeqeq: "off", curly: "error" */

如果一个规则有额外的选项,你可以使用数组字面量指定它们,比如:

1
/* eslint quotes: ["error", "double"], curly: 2 */

更多配置文件的修改项请参考:ESLint - 配置文件

结合自动化工具的使用

比如结合gulp的使用,我们只需要安装gulp-eslint这个插件即可。

gulp是管道流的操作模式,我们只需要在编译js之前进行eslint检测即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const {src, task} = require('gulp');
const eslint = require('gulp-eslint');

task('default', () => {
return src(['scripts/*.js'])
// eslint() attaches the lint output to the "eslint" property
// of the file object so it can be used by other modules.
.pipe(eslint())
// eslint.format() outputs the lint results to the console.
// Alternatively use eslint.formatEach() (see Docs).
.pipe(eslint.format())
// To have the process exit with an error code (1) on
// lint error, return the stream and pipe to failAfterError last.
.pipe(eslint.failAfterError());
});

结合webpack的使用

在webpack中也是一样的逻辑,我们可以安装eslint的loader,也可以使用eslint的插件。

值得注意的是,eslint-loader目前已弃用,作者推荐使用eslint-webpack-plugion来代替。

安装:

1
npm install eslint-webpack-plugin --save-dev

配置:

1
2
3
4
5
6
7
const ESLintPlugin = require('eslint-webpack-plugin');

module.exports = {
// ...
plugins: [new ESLintPlugin(options)],
// ...
};

更多使用细节请参考:webpack-eslint-plugin

参考

谢谢你请我吃糖!